% ------------------------------------------------
% author: Clint Frederickson, Michael Milvich
%
% description:
% This program is an expert system to classify a poker hand
% ------------------------------------------------

% ------------------------------------------------
% Classify an unordered list of cards
% ------------------------------------------------
classify(List) :- msort(List, Sorted), classify2(Sorted).

% ------------------------------------------------
% Classify an ordered list of cards as its highest rating
% ------------------------------------------------
classify2(List) :- straight(List), flush(List), tenFirst(List), write(' royal flush').
classify2(List) :- straight(List), flush(List), write(' straight flush').
classify2(List) :- ofaKind(List, 4), write(' 4 of a kind').
classify2(List) :- fullHouse(List), write(' full house').
classify2(List) :- flush(List), write(' flush').
classify2(List) :- straight(List), write(' straight').
classify2(List) :- ofaKind(List, 3), write(' 3 of a kind').
classify2(List) :- twoPair(List), write(' 2 pair').
classify2(List) :- ofaKind(List, 2), write(' a pair').
classify2(_) :- write(' nothing').

% ------------------------------------------------
% Tests to see if a 10 is the first card
% ------------------------------------------------
tenFirst([X|_]) :- rank(X, Z), Z == 10.

% ------------------------------------------------
% Tests for a full house
% ------------------------------------------------
fullHouse(List) :- ofaKind(List, 3, Used), subtract(List, Used, NewHand), ofaKind(NewHand, 2).
 
% ------------------------------------------------
% Tests for a flush
% ------------------------------------------------
flush([X|Y]) :- suit(X, Z), flush(Y, Z).
flush([], _).
flush([X|Y], SUIT) :- suit(X, Z), Z == SUIT, flush(Y, SUIT).

% ------------------------------------------------
% Tests for a straight
% ------------------------------------------------
straight([X|Y]) :- rank(X, Z), straight(Y, Z).
straight([], _).
straight([X|Y], P_Rank) :- rank(X, Z), P_Rank =:= Z - 1, straight(Y, Z).

% ------------------------------------------------
% Tests for two pair
% ------------------------------------------------
twoPair(List) :- ofaKind(List, 2, Used), subtract(List, Used, NewHand), ofaKind(NewHand, 2).

% ------------------------------------------------
% Tests for 2, 3, 4 of a kind
% ------------------------------------------------
ofaKind(List, Target) :- ofaKind(List, Target, _).
ofaKind([X|Y], Target, UsedCards) :-
   rank(X, Z),
   ofaKind(Y, Z, 1, Target, [X], UsedCards).
   
ofaKind(_, _, Count, Target, WorkingCards, UsedCards) :-
   Count == Target,
   UsedCards = WorkingCards.
   
ofaKind([X|Y], Pval, Count, Target, WorkingCards, UsedCards) :-
   rank(X, Z), Z == Pval,
   NewCount is Count + 1,
   ofaKind(Y, Pval, NewCount, Target, [X|WorkingCards], UsedCards).
   
ofaKind([X|Y], _, _, Target, _, UsedCards) :-
   rank(X, Z),
   ofaKind(Y, Z, 1, Target, [X], UsedCards).

% ------------------------------------------------
% Defines the suit of a card
% ------------------------------------------------
suit(card(_, Suit), X) :- X = Suit.

% ------------------------------------------------
% Defines the rank of the card
% ------------------------------------------------
rank(card(Rank, _), X) :- X = Rank.

