From 442a49ad5a48d417345959b903ae6a6d32d55759 Mon Sep 17 00:00:00 2001 From: Haidong Ji Date: Fri, 15 Apr 2022 15:51:30 -0500 Subject: Great C programming fun Excellent fundamentals and displine training, many tools and techniques exercises: gdb, emacs, valgrind, git --- c3prj2_eval/.gitignore | 4 + c3prj2_eval/Makefile | 10 ++ c3prj2_eval/README | 332 +++++++++++++++++++++++++++++++++++++++++++ c3prj2_eval/README~ | 332 +++++++++++++++++++++++++++++++++++++++++++ c3prj2_eval/cards.c | 131 +++++++++++++++++ c3prj2_eval/cards.h | 38 +++++ c3prj2_eval/deck-c4.o | Bin 0 -> 4752 bytes c3prj2_eval/deck.c | 40 ++++++ c3prj2_eval/deck.c~ | 40 ++++++ c3prj2_eval/deck.h | 22 +++ c3prj2_eval/eval-c4.o | Bin 0 -> 2504 bytes c3prj2_eval/eval.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++ c3prj2_eval/eval.c~ | 366 ++++++++++++++++++++++++++++++++++++++++++++++++ c3prj2_eval/eval.h | 13 ++ c3prj2_eval/future.o | Bin 0 -> 3216 bytes c3prj2_eval/grade.txt | 49 +++++++ c3prj2_eval/input.o | Bin 0 -> 5504 bytes c3prj2_eval/t1.txt | 2 + c3prj2_eval/t2.txt | 2 + c3prj2_eval/t3.txt | 2 + c3prj2_eval/t4.txt | 2 + c3prj2_eval/t5.txt | 2 + c3prj2_eval/test-eval.o | Bin 0 -> 7808 bytes c3prj2_eval/tests.txt | 30 ++++ 24 files changed, 1783 insertions(+) create mode 100644 c3prj2_eval/.gitignore create mode 100644 c3prj2_eval/Makefile create mode 100644 c3prj2_eval/README create mode 100644 c3prj2_eval/README~ create mode 100644 c3prj2_eval/cards.c create mode 100644 c3prj2_eval/cards.h create mode 100644 c3prj2_eval/deck-c4.o create mode 100644 c3prj2_eval/deck.c create mode 100644 c3prj2_eval/deck.c~ create mode 100644 c3prj2_eval/deck.h create mode 100755 c3prj2_eval/eval-c4.o create mode 100644 c3prj2_eval/eval.c create mode 100644 c3prj2_eval/eval.c~ create mode 100644 c3prj2_eval/eval.h create mode 100755 c3prj2_eval/future.o create mode 100644 c3prj2_eval/grade.txt create mode 100755 c3prj2_eval/input.o create mode 100644 c3prj2_eval/t1.txt create mode 100644 c3prj2_eval/t2.txt create mode 100644 c3prj2_eval/t3.txt create mode 100644 c3prj2_eval/t4.txt create mode 100644 c3prj2_eval/t5.txt create mode 100644 c3prj2_eval/test-eval.o create mode 100755 c3prj2_eval/tests.txt (limited to 'c3prj2_eval') diff --git a/c3prj2_eval/.gitignore b/c3prj2_eval/.gitignore new file mode 100644 index 0000000..15d0dfb --- /dev/null +++ b/c3prj2_eval/.gitignore @@ -0,0 +1,4 @@ +deck.o +eval.o +cards.o +test-eval diff --git a/c3prj2_eval/Makefile b/c3prj2_eval/Makefile new file mode 100644 index 0000000..d297bdb --- /dev/null +++ b/c3prj2_eval/Makefile @@ -0,0 +1,10 @@ +CFLAGS=-ggdb3 -Wall -Werror -pedantic -std=gnu99 +GIVEN_OBJS=deck-c4.o eval-c4.o future.o input.o main.o +MY_OBJS=cards.o deck.o eval.o + +test-eval: deck.o eval.o eval-c4.o test-eval.o deck-c4.o cards.o input.o future.o + gcc -o test-eval -ggdb3 deck.o deck-c4.o eval-c4.o eval.o test-eval.o cards.o input.o future.o +poker: $(GIVEN_OBJS) $(MY_OBJS) + gcc -o poker -ggdb3 $(MY_OBJS) $(GIVEN_OBJS) +clean: + rm -f test poker cards.o my-test-main.o *~ diff --git a/c3prj2_eval/README b/c3prj2_eval/README new file mode 100644 index 0000000..ce5530e --- /dev/null +++ b/c3prj2_eval/README @@ -0,0 +1,332 @@ +Hand Evaluation +--------------- +The other part of the project that you will do in this +course is write some of the code to evaluate and compare hands. +Remember that in Course 2 you already wrote test cases +for this code. That means you have already thought +about various corner cases that might come up +and will have a nice suite of tests ready to go +when you finish your code. + +Your ultimate goal in this step is to write a function, +which when passed two hands of cards, determines +which one won (or if they tied). We'll use the +deck_t type that you worked with in the previous +part to represent a hand of cards as well (a hand +of cards is just a much smaller deck of cards---they +are both just sets of cards). + +There are three major steps to determining who won: + (1) Figuring out what ranking each hand has (straight, flush, etc) + If you look in cards.h, you will see enum hand_ranking_t, + which you worked with in Course 2. + (2) Figuring out which 5 cards make up the hand (picking out + the 5 cards that made the flush, or the two pairs and tiebreaker) + (3) Comparing the rankings, and if they are the same, breaking + ties by comparing the values in the hands. + +At this point, you might be thinking that there is going to +be a lot of code to write with all the different possible +arrangements of cards and different possible hand rankings. +However, there are a few important things that will make +this managable: + +(1) You will start by sorting the cards into descending order + by value. This makes it much easier to find straights (cards + in order), and you will have "N of a kind"s grouped together. +(2) The code to find "N of a kind" is basically the same + for 4, 3, and 2 (so we can abstract it out into a function...) +(3) Full house and two pair are just three of a kind and a pair + (so we already have that code...) with another pair + (so we can just write a function to find a secondary pair) +(4) We are going to make two simplifying assumptions: + - if there is a flush, it will occur in at most one suit. + (i.e., you won't have As Ah Kh Qs 8s 7h 4s 3s 3h 2h, + which has two different flushes). + - if there is an ace-high straight, there is not also + an ace-low straight. + (These both hold for all major poker variants) + +If you open up eval.c, you will find the following functions +that you will need to write: + + - int card_ptr_comp(const void * vp1, const void * vp2) + You want to sort the hand by value, so you need + a comparison function to pass to quicksort. + Quicksort sorts into ascending order, but you + want descending order, so you will want to + return + something < 0 if card1 > card2 + 0 if card1 == card2 + something > 0 if card1 < card2 + If two cards have the same value, compare them by + suit in the same order as the enum suit_t: + club < diamond < heart < spade + Note that vp1 and vp2 are passed as const void * + because that is the type that qsort demands. + They will should each be assigned to variables + of type + const card_t * const * cp1 + before using them (this is much like sorting + an array of strings from your readings). + To learn more about using the C library function qsort, + we suggest reviewing the course reading + "Sorting Functions" in the "Function Pointers" + lesson and consulting "man qsort" + to read about the comparison function. + + - suit_t flush_suit(deck_t * hand); + This function looks at the hand and determines + if a flush (at least 5 cards of one suit) exists. + If so, it returns the suit of the cards comprising + the flush. If not, it returns NUM_SUITS. + For example: + Given Ks Qs 0s 9h 8s 7s, it would return SPADES. + Given Kd Qd 0s 9h 8c 7c, it would return NUM_SUITS. + + - unsigned get_largest_element(unsigned * arr, size_t n); + This function returns the largest element in an array + of unsigned integers. This should be familiar + from the videos you watched. + + In course 4 (after you learn to dynamically allocate + memory), you will write get_match_counts, + which will construct an array with one element + per card in the hand. That array will + tell you how many cards in the hand + have the same value as the corresponding + card. You will then use get_largest_element + to figure out which is the best "N of a kind". + + + - size_t get_match_index(unsigned * match_counts, size_t n,unsigned n_of_akind); + This function returns the index in the array (match_counts) whose + value is n_of_akind. The array has n elements. The array match_counts + may have multiple values equal to n_of_akind. You should return + the LOWEST index whose value is n_of_akind [which also guarantees + it corresponds to the largest valued cards, since they will be sorted]. + (Once you figure out the best n_of_akind above, + you will use this to locate that group of cards + in the hand). + Note that it is guaranteed that n_of_akind is in match_counts. + If not, you should abort as this is evidence of an error. + + + - ssize_t find_secondary_pair(deck_t * hand, + unsigned * match_counts, + size_t match_idx) ; + When you have a hand with 3 of a kind or a pair, + you will want to look and see if there is another + pair to make the hand into a full house or + or two pairs. This function takes in + the hand, the match counts from before, and + the index where the original match (3 of a kind + or pair) was found. It should find + the index of a card meeting the following conditions: + - Its match count is > 1 [so there is at least a pair of them] + - The card's value is not the same as the value of the + card at match_idx (so it is not part of the original + three of a kind/pair) + - It is the lowest index meeting the first two conditions + (which will be the start of that pair, and the highest + value pair other than the original match). + If no such index can be found, this function should + return -1. + + - int is_straight_at(deck_t * hand, size_t index, suit_t fs) + This function should determine if there is a straight + starting at index (and only starting at index) in the + given hand. If fs is NUM_SUITS, then it should look + for any straight. If fs is some other value, then + it should look for a straight flush in the specified suit. + This function should return: + -1 if an Ace-low straight was found at that index (and that index is the Ace) + 0 if no straight was found at that index + 1 if any other straight was found at that index + + When writing this function, you can assume + that the hand is sorted by value: the + values of cards will appear in descending order. + (A K Q ... 4 3 2). + + There are two things that make this function + tricky (probably the trickiest function in + this assignment): + (1) Ace low straights. An Ace low straight + will appear in the hand with the Ace + first, then possibly some other cards, + then the 5 4 3 2. For example, you + might have + As Ks Qc 5s 4c 3d 2c + (2) You may have multiple cards with the + same value, but still have a straight: + As Ac Ks Kc Qh Jh 0d + has a straight even though A K Q + do not appear next to each other in + our sorted order. + Hint: I made this easier on myself, by writing + two helper functions: + int is_n_length_straight_at(deck_t * hand, size_t index, suit_t fs, int n) ; + and + int is_ace_low_straight_at(deck_t * hand, size_t index, suit_t fs); + + The second of these lets me pull out the complexities of an ace + low straight. However, in doing so, I realized that there + would be a lot of duplication of code between the ace low straight + helper and the original function (for an ace low, you want to find + a 5, then a straight of length 4: 5, 4, 3, 2). This realization + caused me to pull out much of the code into is_n_length_straight_at, + so that I could call it with n=4 to search for the 5,4,3,2 part + of an ace low straight. + + + - hand_eval_t build_hand_from_match(deck_t * hand, + unsigned n, + hand_ranking_t what, + size_t idx) ; + Now you have written a bunch of functions that + will figure out which ranking a hand has. It + is time to construct a hand_eval_t (see eval.h) which + has the ranking and the 5 cards used for it. + This helper function will handle the + "n of a kind" case. + It should make hand_eval_t and set its + ranking to the passed in "what" value. + Then it should copy "n" cards from + the hand, starting at "idx" into + the first "n" elements of the hand_eval_t's + "cards" array. The cards field in + hand_eval_t is declared as: + card_t * cards[5] + This is an array of pointers, each to a card_t. + Draw a picture to be sure you know how to name + each card_t "box" before you start writing code. + + Your function should then fill the remainder + of the "cards" array with the highest-value + cards from the hand which were not in + the "n of a kind". + + For example, given this hand: + As Kc Kh Kd Qc 8s 5d + The hand has 3 kings, and the As and Qc will break ties. + Note that here n = 3, what= THREE_OF_A_KIND, idx= 1. + So the cards array in the hand_eval_t should have + + Kc Kh Kd As Qc + + Note that what may also be FULL_HOUSE or TWO_PAIR, + since this function will get used for the first + part of preparing those evaluations (then other code + will later fix the rest of the hand with the other pair). + + + - int compare_hands(deck_t * hand1, deck_t * hand2) + + This is the goal of the whole thing: given two hands, + figure out which wins (or if it is a tie). + Everything you wrote goes together to make this work! + + + We're providing you with + hand_eval_t evaluate_hand(deck_t * hand) ; + since it involves some things you won't learn until + Course 4. It's also not super interesting: + it mostly make a bunch of calls to the functions + you wrote above, and has a lot of if-statements + to handle the rules of poker. + + The important part of evaluate_hand is that + (a) assumes the cards in the passed in hand are + sorted and (b) it returns a hand_eval_t for the passed in hand. + + That means that to implement compare_hands, you should + + (a) sort each hand using qsort on the hand's cards + and your card_ptr_comp [from earlier] + (b) Call evaluate_hand on each hand, which gives you a hand_eval_t + for each hand. + (c) Check if the rankings in the hand_eval_t are the same + or different. If they are different, you can just use + the ranking to determine the winner. + (d) If they are the same, then you need to look + at the values in the cards array of each hand_eval_t + to break the tie. The way that we constructed + the hand_eval_t's cards array means that + the cards are already ordered from most significant (at index 0) + to least significant (at index 4). You can just + do a lexicographic comparison on the values in the arrays. + (Its like comparing strings, but you are comparing values + of cards---if element 0 is the different, use that difference + to determine your answer. If element 0 is the same, + look at element 1, and so on). +Note that compare hands should return a positive number +if hand 1 is better, 0 if the hands tie, and a negative number +if hand 2 is better. + +You will also notice some functions at the bottom of eval.c that +we provided. You don't need to do anything to these---we wrote +them for you to keep the amount of code manageable. + +-------------- +That sure was a lot of code! You've been compiling and testing +along the way, right? We sure hope so :) + +However, to help you test things out even more, we've +provided some test infrastructure for you. + +If you do + +make + +You will compile the test-eval program you are +familiar with from Course 2. This program behaves +exactly like it did in Course 2. As a reminder, +it expects input where each line looks like: + +hand1 ; hand2 + +where a hand looks like something that print_hand +would output. So a valid input might be + +Kc Ac Jh 8s 9c 2s ; Ah Kh 0s 7c 7h 3c + +For each line in the input, the test program +will tell you: + - The results of your functions that went into evaluating it + (if there was a straight, if there was a flush, etc). + - What hand_eval_t was returned by evaluate_hand for each hand + - Which hand won (or if it was a tie) according to compare_hands + +Good thing you have all those test cases from Course 2 +to use with it! + +Because you have an object file test-eval.o and not the source +test-eval.c, you may need to use the debugger differently than you're +used to. For this test program, we recommend running gdb in emacs, +then first, specifying the command line argument (in this case, a file +name with your tests) + +set args tests.txt + +Then you will want to set a breakpoint in the code you wrote (since +you can't see test-eval.c, where main is, to step through it). For +example, if you just wrote the function is_straight_at, and it doesn't +behave the way you expect, you can do + +break is_straight_at + +and gdb will pause execution any time the program calls that +function. Then you can use the command "run" instead of "start," since +you don't need to pause execution at the start of main. Also recall +the command "continue," which you can review from the debugging +lesson. + + +As usual, when you are finished, use the "grade" command. +When you pass, this, congratulations! You are done +with Course 3 and ready to move on to Course 4 :) + + + + diff --git a/c3prj2_eval/README~ b/c3prj2_eval/README~ new file mode 100644 index 0000000..1b77db5 --- /dev/null +++ b/c3prj2_eval/README~ @@ -0,0 +1,332 @@ +Hand Evaluation +--------------- +The other part of the project that you will do in this +course is write some of the code to evaluate and compare hands. +Remember that in Course 2 you already wrote test cases +for this code. That means you have already thought +about various corner cases that might come up +and will have a nice suite of tests ready to go +when you finish your code. + +Your ultimate goal in this step is to write a function, +which when passed two hands of cards, determines +which one won (or if they tied). We'll use the +deck_t type that you worked with in the previous +part to represent a hand of cards as well (a hand +of cards is just a much smaller deck of cards---they +are both just sets of cards). + +There are three major steps to determining who won: + (1) Figuring out what ranking each hand has (straight, flush, etc) + If you look in cards.h, you will see enum hand_ranking_t, + which you worked with in Course 2. + (2) Figuring out which 5 cards make up the hand (picking out + the 5 cards that made the flush, or the two pairs and tiebreaker) + (3) Comparing the rankings, and if they are the same, breaking + ties by comparing the values in the hands. + +At this point, you might be thinking that there is going to +be a lot of code to write with all the different possible +arrangements of cards and different possible hand rankings. +However, there are a few important things that will make +this managable: + +(1) You will start by sorting the cards into descending order + by value. This makes it much easier to find straights (cards + in order), and you will have "N of a kind"s grouped together. +(2) The code to find "N of a kind" is basically the same + for 4, 3, and 2 (so we can abstract it out into a function...) +(3) Full house and two pair are just three of a kind and a pair + (so we already have that code...) with another pair + (so we can just write a function to find a secondary pair) +(4) We are going to make two simplifying assumptions: + - if there is a flush, it will occur in at most one suit. + (i.e., you won't have As Ah Kh Qs 8s 7h 4s 3s 3h 2h, + which has two different flushes). + - if there is an ace-high straight, there is not also + an ace-low straight. + (These both hold for all major poker variants) + +If you open up eval.c, you will find the following functions +that you will need to write: + + - int card_ptr_comp(const void * vp1, const void * vp2) + You want to sort the hand by value, so you need + a comparison function to pass to quicksort. + Quicksort sorts into ascending order, but you + want descending order, so you will want to + return + something < 0 if card1 > card2 + 0 if card1 == card2 + something > 0 if card1 < card2 + If two cards have the same value, compare them by + suit in the same order as the enum suit_t: + club < diamond < heart < spade + Note that vp1 and vp2 are passed as const void * + because that is the type that qsort demands. + They will should each be assigned to variables + of type + const card_t * const * cp1 + before using them (this is much like sorting + an array of strings from your readings). + To learn more about using the C library function qsort, + we suggest reviewing the course reading + "Sorting Functions" in the "Function Pointers" + lesson and consulting "man qsort" + to read about the comparison function. + + - suit_t flush_suit(deck_t * hand); + This function looks at the hand and determines + if a flush (at least 5 cards of one suit) exists. + If so, it returns the suit of the cards comprising + the flush. If not, it returns NUM_SUITS. + For example: + Given Ks Qs 0s 9h 8s 7s, it would return SPADES. + Given Kd Qd 0s 9h 8c 7c, it would return NUM_SUITS. + + - unsigned get_largest_element(unsigned * arr, size_t n); + This function returns the largest element in an array + of unsigned integers. This should be familiar + from the videos you watched. + + In course 4 (after you learn to dynamically allocate + memory), you will write get_match_counts, + which will construct an array with one element + per card in the hand. That array will + tell you how many cards in the hand + have the same value as the corresponding + card. You will then use get_largest_element + to figure out which is the best "N of a kind". + + + - size_t get_match_index(unsigned * match_counts, size_t n,unsigned n_of_akind); + This function returns the index in the array (match_counts) whose + value is n_of_akind. The array has n elements. The array match_counts + may have multiple values equal to n_of_akind. You should return + the LOWEST index whose value is n_of_akind [which also guarantees + it corresponds to the largest valued cards, since they will be sorted]. + (Once you figure out the best n_of_akind above, + you will use this to locate that group of cards + in the hand). + Note that it is guaranteed that n_of_akind is in match_counts. + If not, you should abort as this is evidence of an error. + + + - size_t find_secondary_pair(deck_t * hand, + unsigned * match_counts, + size_t match_idx) ; + When you have a hand with 3 of a kind or a pair, + you will want to look and see if there is another + pair to make the hand into a full house or + or two pairs. This function takes in + the hand, the match counts from before, and + the index where the original match (3 of a kind + or pair) was found. It should find + the index of a card meeting the following conditions: + - Its match count is > 1 [so there is at least a pair of them] + - The card's value is not the same as the value of the + card at match_idx (so it is not part of the original + three of a kind/pair) + - It is the lowest index meeting the first two conditions + (which will be the start of that pair, and the highest + value pair other than the original match). + If no such index can be found, this function should + return -1. + + - int is_straight_at(deck_t * hand, size_t index, suit_t fs) + This function should determine if there is a straight + starting at index (and only starting at index) in the + given hand. If fs is NUM_SUITS, then it should look + for any straight. If fs is some other value, then + it should look for a straight flush in the specified suit. + This function should return: + -1 if an Ace-low straight was found at that index (and that index is the Ace) + 0 if no straight was found at that index + 1 if any other straight was found at that index + + When writing this function, you can assume + that the hand is sorted by value: the + values of cards will appear in descending order. + (A K Q ... 4 3 2). + + There are two things that make this function + tricky (probably the trickiest function in + this assignment): + (1) Ace low straights. An Ace low straight + will appear in the hand with the Ace + first, then possibly some other cards, + then the 5 4 3 2. For example, you + might have + As Ks Qc 5s 4c 3d 2c + (2) You may have multiple cards with the + same value, but still have a straight: + As Ac Ks Kc Qh Jh 0d + has a straight even though A K Q + do not appear next to each other in + our sorted order. + Hint: I made this easier on myself, by writing + two helper functions: + int is_n_length_straight_at(deck_t * hand, size_t index, suit_t fs, int n) ; + and + int is_ace_low_straight_at(deck_t * hand, size_t index, suit_t fs); + + The second of these lets me pull out the complexities of an ace + low straight. However, in doing so, I realized that there + would be a lot of duplication of code between the ace low straight + helper and the original function (for an ace low, you want to find + a 5, then a straight of length 4: 5, 4, 3, 2). This realization + caused me to pull out much of the code into is_n_length_straight_at, + so that I could call it with n=4 to search for the 5,4,3,2 part + of an ace low straight. + + + - hand_eval_t build_hand_from_match(deck_t * hand, + unsigned n, + hand_ranking_t what, + size_t idx) ; + Now you have written a bunch of functions that + will figure out which ranking a hand has. It + is time to construct a hand_eval_t (see eval.h) which + has the ranking and the 5 cards used for it. + This helper function will handle the + "n of a kind" case. + It should make hand_eval_t and set its + ranking to the passed in "what" value. + Then it should copy "n" cards from + the hand, starting at "idx" into + the first "n" elements of the hand_eval_t's + "cards" array. The cards field in + hand_eval_t is declared as: + card_t * cards[5] + This is an array of pointers, each to a card_t. + Draw a picture to be sure you know how to name + each card_t "box" before you start writing code. + + Your function should then fill the remainder + of the "cards" array with the highest-value + cards from the hand which were not in + the "n of a kind". + + For example, given this hand: + As Kc Kh Kd Qc 8s 5d + The hand has 3 kings, and the As and Qc will break ties. + Note that here n = 3, what= THREE_OF_A_KIND, idx= 1. + So the cards array in the hand_eval_t should have + + Kc Kh Kd As Qc + + Note that what may also be FULL_HOUSE or TWO_PAIR, + since this function will get used for the first + part of preparing those evaluations (then other code + will later fix the rest of the hand with the other pair). + + + - int compare_hands(deck_t * hand1, deck_t * hand2) + + This is the goal of the whole thing: given two hands, + figure out which wins (or if it is a tie). + Everything you wrote goes together to make this work! + + + We're providing you with + hand_eval_t evaluate_hand(deck_t * hand) ; + since it involves some things you won't learn until + Course 4. It's also not super interesting: + it mostly make a bunch of calls to the functions + you wrote above, and has a lot of if-statements + to handle the rules of poker. + + The important part of evaluate_hand is that + (a) assumes the cards in the passed in hand are + sorted and (b) it returns a hand_eval_t for the passed in hand. + + That means that to implement compare_hands, you should + + (a) sort each hand using qsort on the hand's cards + and your card_ptr_comp [from earlier] + (b) Call evaluate_hand on each hand, which gives you a hand_eval_t + for each hand. + (c) Check if the rankings in the hand_eval_t are the same + or different. If they are different, you can just use + the ranking to determine the winner. + (d) If they are the same, then you need to look + at the values in the cards array of each hand_eval_t + to break the tie. The way that we constructed + the hand_eval_t's cards array means that + the cards are already ordered from most significant (at index 0) + to least significant (at index 4). You can just + do a lexicographic comparison on the values in the arrays. + (Its like comparing strings, but you are comparing values + of cards---if element 0 is the different, use that difference + to determine your answer. If element 0 is the same, + look at element 1, and so on). +Note that compare hands should return a positive number +if hand 1 is better, 0 if the hands tie, and a negative number +if hand 2 is better. + +You will also notice some functions at the bottom of eval.c that +we provided. You don't need to do anything to these---we wrote +them for you to keep the amount of code manageable. + +-------------- +That sure was a lot of code! You've been compiling and testing +along the way, right? We sure hope so :) + +However, to help you test things out even more, we've +provided some test infrastructure for you. + +If you do + +make + +You will compile the test-eval program you are +familiar with from Course 2. This program behaves +exactly like it did in Course 2. As a reminder, +it expects input where each line looks like: + +hand1 ; hand2 + +where a hand looks like something that print_hand +would output. So a valid input might be + +Kc Ac Jh 8s 9c 2s ; Ah Kh 0s 7c 7h 3c + +For each line in the input, the test program +will tell you: + - The results of your functions that went into evaluating it + (if there was a straight, if there was a flush, etc). + - What hand_eval_t was returned by evaluate_hand for each hand + - Which hand won (or if it was a tie) according to compare_hands + +Good thing you have all those test cases from Course 2 +to use with it! + +Because you have an object file test-eval.o and not the source +test-eval.c, you may need to use the debugger differently than you're +used to. For this test program, we recommend running gdb in emacs, +then first, specifying the command line argument (in this case, a file +name with your tests) + +set args tests.txt + +Then you will want to set a breakpoint in the code you wrote (since +you can't see test-eval.c, where main is, to step through it). For +example, if you just wrote the function is_straight_at, and it doesn't +behave the way you expect, you can do + +break is_straight_at + +and gdb will pause execution any time the program calls that +function. Then you can use the command "run" instead of "start," since +you don't need to pause execution at the start of main. Also recall +the command "continue," which you can review from the debugging +lesson. + + +As usual, when you are finished, use the "grade" command. +When you pass, this, congratulations! You are done +with Course 3 and ready to move on to Course 4 :) + + + + diff --git a/c3prj2_eval/cards.c b/c3prj2_eval/cards.c new file mode 100644 index 0000000..8e467ec --- /dev/null +++ b/c3prj2_eval/cards.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include "cards.h" + + +void assert_card_valid(card_t c) { + assert(c.value >= 2 && c.value <= VALUE_ACE); + assert(c.suit >= SPADES && c.suit <= CLUBS); +} + +const char * ranking_to_string(hand_ranking_t r) { + switch (r) { + case STRAIGHT_FLUSH: + return "STRAIGHT_FLUSH"; + case FOUR_OF_A_KIND: + return "FOUR_OF_A_KIND"; + case FULL_HOUSE: + return "FULL_HOUSE"; + case FLUSH: + return "FLUSH"; + case STRAIGHT: + return "STRAIGHT"; + case THREE_OF_A_KIND: + return "THREE_OF_A_KIND"; + case TWO_PAIR: + return "TWO_PAIR"; + case PAIR: + return "PAIR"; + default: + return "NOTHING"; + } +} + +char value_letter(card_t c) { + switch(c.value) { + case VALUE_ACE: + return 'A'; + case VALUE_KING: + return 'K'; + case VALUE_QUEEN: + return 'Q'; + case VALUE_JACK: + return 'J'; + case 10: + return '0'; + default: + return '0' + c.value; + } +} + + +char suit_letter(card_t c) { + switch(c.suit) { + case SPADES: + return 's'; + case HEARTS: + return 'h'; + case DIAMONDS: + return 'd'; + default: + return 'c'; + } + +} + +void print_card(card_t c) { + printf("%c%c", value_letter(c), suit_letter(c)); +} + +card_t card_from_letters(char value_let, char suit_let) { + card_t temp; + switch(value_let) { + case 'A': + temp.value = VALUE_ACE; + break; + case 'K': + temp.value = VALUE_KING; + break; + case 'Q': + temp.value = VALUE_QUEEN; + break; + case 'J': + temp.value = VALUE_JACK; + break; + case '0': + temp.value = 10; + break; + default: + temp.value = value_let - '0'; + break; + } + switch(suit_let) { + case 's': + temp.suit = SPADES; + break; + case 'h': + temp.suit = HEARTS; + break; + case 'd': + temp.suit = DIAMONDS; + break; + case 'c': + temp.suit = CLUBS; + break; + } + assert_card_valid(temp); + return temp; +} + +card_t card_from_num(unsigned c) { + card_t temp; + if (c <= 12) { + temp.value = c + 2; + temp.suit = SPADES; + } + else if (c > 12 && c <= 25) { + temp.value = c % 13 + 2; + temp.suit = HEARTS; + } + else if (c > 25 && c <= 38) { + temp.value = c % 13 + 2; + temp.suit = DIAMONDS; + } + else { + temp.value = c % 13 + 2; + temp.suit = CLUBS; + } + assert_card_valid(temp); + return temp; +} diff --git a/c3prj2_eval/cards.h b/c3prj2_eval/cards.h new file mode 100644 index 0000000..fef0529 --- /dev/null +++ b/c3prj2_eval/cards.h @@ -0,0 +1,38 @@ +#ifndef CARD_H +#define CARD_H +#define VALUE_ACE 14 +#define VALUE_KING 13 +#define VALUE_QUEEN 12 +#define VALUE_JACK 11 +typedef enum { + SPADES, + HEARTS, + DIAMONDS, + CLUBS, + NUM_SUITS +} suit_t; + +struct card_tag { + unsigned value; + suit_t suit; +}; +typedef struct card_tag card_t; +typedef enum { + STRAIGHT_FLUSH, + FOUR_OF_A_KIND, + FULL_HOUSE, + FLUSH, + STRAIGHT, + THREE_OF_A_KIND, + TWO_PAIR, + PAIR, + NOTHING +} hand_ranking_t; +card_t card_from_num(unsigned c); +void assert_card_valid(card_t c); +const char * ranking_to_string(hand_ranking_t r) ; +char value_letter(card_t c); +char suit_letter(card_t c) ; +void print_card(card_t c); +card_t card_from_letters(char value_let, char suit_let); +#endif diff --git a/c3prj2_eval/deck-c4.o b/c3prj2_eval/deck-c4.o new file mode 100644 index 0000000..5fc5541 Binary files /dev/null and b/c3prj2_eval/deck-c4.o differ diff --git a/c3prj2_eval/deck.c b/c3prj2_eval/deck.c new file mode 100644 index 0000000..c4a3d78 --- /dev/null +++ b/c3prj2_eval/deck.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include "deck.h" + +void print_hand(deck_t * hand){ + for (size_t i = 0; i < hand->n_cards; i++) { + print_card(*hand->cards[i]); + printf(" "); + } +} + +int deck_contains(deck_t * d, card_t c) { + for (size_t i = 0; i < d->n_cards; i++) { + if (d->cards[i]->value == c.value && d->cards[i]->suit == c.suit ) { + return 1; + } + } + return 0; +} + +void shuffle(deck_t * d){ + card_t c; + for (size_t i = d->n_cards; i > 0; i--) { + int r = rand() % i; + c = *d->cards[i-1]; + *d->cards[i-1] = *d->cards[r]; + //d[i-1] = d[r]; + *d->cards[r] = c; + } +} + +void assert_full_deck(deck_t * d) { + card_t temp; + for (int i = 0; i <= 51; i++) { + temp = card_from_num(i); + assert(deck_contains(d, temp) == 1); + } + +} diff --git a/c3prj2_eval/deck.c~ b/c3prj2_eval/deck.c~ new file mode 100644 index 0000000..c4a3d78 --- /dev/null +++ b/c3prj2_eval/deck.c~ @@ -0,0 +1,40 @@ +#include +#include +#include +#include "deck.h" + +void print_hand(deck_t * hand){ + for (size_t i = 0; i < hand->n_cards; i++) { + print_card(*hand->cards[i]); + printf(" "); + } +} + +int deck_contains(deck_t * d, card_t c) { + for (size_t i = 0; i < d->n_cards; i++) { + if (d->cards[i]->value == c.value && d->cards[i]->suit == c.suit ) { + return 1; + } + } + return 0; +} + +void shuffle(deck_t * d){ + card_t c; + for (size_t i = d->n_cards; i > 0; i--) { + int r = rand() % i; + c = *d->cards[i-1]; + *d->cards[i-1] = *d->cards[r]; + //d[i-1] = d[r]; + *d->cards[r] = c; + } +} + +void assert_full_deck(deck_t * d) { + card_t temp; + for (int i = 0; i <= 51; i++) { + temp = card_from_num(i); + assert(deck_contains(d, temp) == 1); + } + +} diff --git a/c3prj2_eval/deck.h b/c3prj2_eval/deck.h new file mode 100644 index 0000000..f5f44bf --- /dev/null +++ b/c3prj2_eval/deck.h @@ -0,0 +1,22 @@ +#ifndef DECK_H +#define DECK_H +#include +#include "cards.h" +struct deck_tag { + card_t ** cards; + size_t n_cards; +}; +typedef struct deck_tag deck_t; + +void print_hand(deck_t * hand); +int deck_contains(deck_t * d, card_t c) ; +void shuffle(deck_t * d); +void assert_full_deck(deck_t * d) ; +//The below functions will be done in course 4. +deck_t * make_deck_exclude(deck_t * excluded_cards); +void add_card_to(deck_t * deck, card_t c); +card_t * add_empty_card(deck_t * deck); +void free_deck(deck_t * deck) ; +deck_t * build_remaining_deck(deck_t ** hands, size_t n_hands) ; +#endif +// diff --git a/c3prj2_eval/eval-c4.o b/c3prj2_eval/eval-c4.o new file mode 100755 index 0000000..8cca00b Binary files /dev/null and b/c3prj2_eval/eval-c4.o differ diff --git a/c3prj2_eval/eval.c b/c3prj2_eval/eval.c new file mode 100644 index 0000000..b4bb37a --- /dev/null +++ b/c3prj2_eval/eval.c @@ -0,0 +1,366 @@ +#include "eval.h" +#include +#include +#include + +int card_ptr_comp(const void *vp1, const void *vp2) { + const card_t *const *cp1 = vp1; + const card_t *const *cp2 = vp2; + + if ((*cp1)->value != (*cp2)->value) { + return (*cp2)->value - (*cp1)->value; + } + + return (*cp2)->suit - (*cp1)->suit; +} + +suit_t flush_suit(deck_t *hand) { + int numClub = 0; + int numDiamond = 0; + int numHeart = 0; + int numSpade = 0; + + for (size_t i = 0; i < hand->n_cards; i++) { + switch (hand->cards[i]->suit) { + case SPADES: + numSpade++; + break; + case HEARTS: + numHeart++; + break; + case DIAMONDS: + numDiamond++; + break; + default: + numClub++; + } + } + + if (numSpade >= 5) { + return SPADES; + } + if (numHeart >= 5) { + return HEARTS; + } + if (numDiamond >= 5) { + return DIAMONDS; + } + if (numClub >= 5) { + return CLUBS; + } + return NUM_SUITS; +} + +unsigned get_largest_element(unsigned *arr, size_t n) { + if (n == 0) { + return 0; + } + unsigned ans = arr[0]; + for (size_t i = 1; i < n; i++) { + if (arr[i] > ans) { + ans = arr[i]; + } + } + return ans; +} + +size_t get_match_index(unsigned *match_counts, size_t n, unsigned n_of_akind) { + + for (size_t i = 0; i < n; i++) { + if (match_counts[i] == n_of_akind) { + return i; + } + } + assert(1 == 2); +} + +ssize_t find_secondary_pair(deck_t *hand, unsigned *match_counts, + size_t match_idx) { + + for (size_t i = 0; i < hand->n_cards; i++) { + if (match_counts[i] > 1) { + if (hand->cards[i]->value != hand->cards[match_idx]->value) { + return i; + } + } + } + return -1; +} + +int is_ace_low_straight_at_five(deck_t *hand, size_t index, suit_t fs) { + int straightTally = 2; + int currentCardValue = 5; + for (size_t i = index + 1; i < hand->n_cards; i++) { + if (straightTally == 5) { + return -1; + } + + if (hand->cards[i]->value == currentCardValue) { + continue; + } + + if (hand->cards[i]->value == currentCardValue - 1) { + if (fs == NUM_SUITS || hand->cards[i]->suit == fs) { + straightTally++; + currentCardValue--; + } else { + continue; + } + } else { + break; + } + } + if (straightTally == 5) { + return -1; + } + return 0; +} + +int is_ace_low_straight_at(deck_t *hand, size_t index, suit_t fs) { + int currentCardValue = hand->cards[index]->value; + if (currentCardValue == 5) { + return is_ace_low_straight_at_five(hand, index, fs); + } + + if (currentCardValue == VALUE_ACE) { + for (size_t i = index + 1; i < hand->n_cards; i++) { + + if (hand->cards[i]->value == currentCardValue) { + continue; + } + + if (hand->cards[i]->value == 5) { + return is_ace_low_straight_at_five(hand, i, fs); + } + } + } + + return 0; +} + +int is_straight_at(deck_t *hand, size_t index, suit_t fs) { + if (hand->cards[index]->suit != fs && fs != NUM_SUITS) { + return 0; + } + int currentCardValue = hand->cards[index]->value; + if (currentCardValue < 5) { + return 0; + } + if (currentCardValue == 5) { + if (hand->cards[0]->value != 14) { + return 0; + } else { + return is_ace_low_straight_at(hand, index, fs); + } + } + + int straightTally = 1; + for (size_t i = index + 1; i < hand->n_cards; i++) { + if (straightTally == 5) { + return 1; + } + + if (hand->cards[i]->value == currentCardValue) { + continue; + } + + if (hand->cards[i]->value == currentCardValue - 1) { + if (fs == NUM_SUITS || hand->cards[i]->suit == fs) { + straightTally++; + currentCardValue--; + } else { + continue; + } + } else { + break; + } + } + if (straightTally == 5) { + return 1; + } + + if (hand->cards[index]->value == VALUE_ACE) { + return is_ace_low_straight_at(hand, index, fs); + } else { + return 0; + } +} + +hand_eval_t build_hand_from_match(deck_t *hand, unsigned n, hand_ranking_t what, + size_t idx) { + + hand_eval_t ans; + ans.ranking = what; + for (int i = 0; i < n; i++) { + ans.cards[i] = hand->cards[idx + i]; + } + + if (idx == 0) { + for (int i = n; i < 5; i++) { + ans.cards[i] = hand->cards[i]; + } + } else if (idx >= 5 - n) { + for (int i = 0; i < 5 - n; i++) { + ans.cards[n + i] = hand->cards[i]; + } + } else { + for (int i = 0; i < idx; i++) { + ans.cards[n + i] = hand->cards[i]; + } + for (int i = idx + n; i < 5; i++) { + ans.cards[i] = hand->cards[i]; + } + } + return ans; +} + +int compare_hands(deck_t *hand1, deck_t *hand2) { + + qsort(hand1->cards, hand1->n_cards, sizeof(card_t), card_ptr_comp); + qsort(hand2->cards, hand2->n_cards, sizeof(card_t), card_ptr_comp); + hand_eval_t ht1 = evaluate_hand(hand1); + hand_eval_t ht2 = evaluate_hand(hand2); + + if (ht1.ranking != ht2.ranking) { + return ht2.ranking - ht1.ranking; + } + + for (int i = 0; i < 5; i++) { + if (ht1.cards[i]->value == ht2.cards[i]->value) { + continue; + } else { + return ht1.cards[i]->value - ht2.cards[i]->value; + } + } + return 0; +} + +// You will write this function in Course 4. +// For now, we leave a prototype (and provide our +// implementation in eval-c4.o) so that the +// other functions we have provided can make +// use of get_match_counts. +unsigned *get_match_counts(deck_t *hand); + +// We provide the below functions. You do NOT need to modify them +// In fact, you should not modify them! + +// This function copies a straight starting at index "ind" from deck "from". +// This copies "count" cards (typically 5). +// into the card array "to" +// if "fs" is NUM_SUITS, then suits are ignored. +// if "fs" is any other value, a straight flush (of that suit) is copied. +void copy_straight(card_t **to, deck_t *from, size_t ind, suit_t fs, + size_t count) { + assert(fs == NUM_SUITS || from->cards[ind]->suit == fs); + unsigned nextv = from->cards[ind]->value; + size_t to_ind = 0; + while (count > 0) { + assert(ind < from->n_cards); + assert(nextv >= 2); + assert(to_ind < 5); + if (from->cards[ind]->value == nextv && + (fs == NUM_SUITS || from->cards[ind]->suit == fs)) { + to[to_ind] = from->cards[ind]; + to_ind++; + count--; + nextv--; + } + ind++; + } +} + +// This looks for a straight (or straight flush if "fs" is not NUM_SUITS) +// in "hand". It calls the student's is_straight_at for each possible +// index to do the work of detecting the straight. +// If one is found, copy_straight is used to copy the cards into +// "ans". +int find_straight(deck_t *hand, suit_t fs, hand_eval_t *ans) { + if (hand->n_cards < 5) { + return 0; + } + for (size_t i = 0; i <= hand->n_cards - 5; i++) { + int x = is_straight_at(hand, i, fs); + if (x != 0) { + if (x < 0) { // ace low straight + assert(hand->cards[i]->value == VALUE_ACE && + (fs == NUM_SUITS || hand->cards[i]->suit == fs)); + ans->cards[4] = hand->cards[i]; + size_t cpind = i + 1; + while (hand->cards[cpind]->value != 5 || + !(fs == NUM_SUITS || hand->cards[cpind]->suit == fs)) { + cpind++; + assert(cpind < hand->n_cards); + } + copy_straight(ans->cards, hand, cpind, fs, 4); + } else { + copy_straight(ans->cards, hand, i, fs, 5); + } + return 1; + } + } + return 0; +} + +// This function puts all the hand evaluation logic together. +// This function is longer than we generally like to make functions, +// and is thus not so great for readability :( +hand_eval_t evaluate_hand(deck_t *hand) { + suit_t fs = flush_suit(hand); + hand_eval_t ans; + if (fs != NUM_SUITS) { + if (find_straight(hand, fs, &ans)) { + ans.ranking = STRAIGHT_FLUSH; + return ans; + } + } + unsigned *match_counts = get_match_counts(hand); + unsigned n_of_a_kind = get_largest_element(match_counts, hand->n_cards); + assert(n_of_a_kind <= 4); + size_t match_idx = get_match_index(match_counts, hand->n_cards, n_of_a_kind); + ssize_t other_pair_idx = find_secondary_pair(hand, match_counts, match_idx); + free(match_counts); + if (n_of_a_kind == 4) { // 4 of a kind + return build_hand_from_match(hand, 4, FOUR_OF_A_KIND, match_idx); + } else if (n_of_a_kind == 3 && other_pair_idx >= 0) { // full house + ans = build_hand_from_match(hand, 3, FULL_HOUSE, match_idx); + ans.cards[3] = hand->cards[other_pair_idx]; + ans.cards[4] = hand->cards[other_pair_idx + 1]; + return ans; + } else if (fs != NUM_SUITS) { // flush + ans.ranking = FLUSH; + size_t copy_idx = 0; + for (size_t i = 0; i < hand->n_cards; i++) { + if (hand->cards[i]->suit == fs) { + ans.cards[copy_idx] = hand->cards[i]; + copy_idx++; + if (copy_idx >= 5) { + break; + } + } + } + return ans; + } else if (find_straight(hand, NUM_SUITS, &ans)) { // straight + ans.ranking = STRAIGHT; + return ans; + } else if (n_of_a_kind == 3) { // 3 of a kind + return build_hand_from_match(hand, 3, THREE_OF_A_KIND, match_idx); + } else if (other_pair_idx >= 0) { // two pair + assert(n_of_a_kind == 2); + ans = build_hand_from_match(hand, 2, TWO_PAIR, match_idx); + ans.cards[2] = hand->cards[other_pair_idx]; + ans.cards[3] = hand->cards[other_pair_idx + 1]; + if (match_idx > 0) { + ans.cards[4] = hand->cards[0]; + } else if (other_pair_idx > 2) { // if match_idx is 0, first pair is in 01 + // if other_pair_idx > 2, then, e.g. A A K Q Q + ans.cards[4] = hand->cards[2]; + } else { // e.g., A A K K Q + ans.cards[4] = hand->cards[4]; + } + return ans; + } else if (n_of_a_kind == 2) { + return build_hand_from_match(hand, 2, PAIR, match_idx); + } + return build_hand_from_match(hand, 0, NOTHING, 0); +} diff --git a/c3prj2_eval/eval.c~ b/c3prj2_eval/eval.c~ new file mode 100644 index 0000000..5e880a2 --- /dev/null +++ b/c3prj2_eval/eval.c~ @@ -0,0 +1,366 @@ +#include "eval.h" +#include +#include +#include + +int card_ptr_comp(const void *vp1, const void *vp2) { + const card_t *const *cp1 = vp1; + const card_t *const *cp2 = vp2; + + if ((*cp1)->value != (*cp2)->value) { + return (*cp2)->value - (*cp1)->value; + } + + return (*cp2)->suit - (*cp1)->suit; +} + +suit_t flush_suit(deck_t *hand) { + int numClub = 0; + int numDiamond = 0; + int numHeart = 0; + int numSpade = 0; + + for (size_t i = 0; i < hand->n_cards; i++) { + switch (hand->cards[i]->suit) { + case SPADES: + numSpade++; + break; + case HEARTS: + numHeart++; + break; + case DIAMONDS: + numDiamond++; + break; + default: + numClub++; + } + } + + if (numSpade >= 5) { + return SPADES; + } + if (numHeart >= 5) { + return HEARTS; + } + if (numDiamond >= 5) { + return DIAMONDS; + } + if (numClub >= 5) { + return CLUBS; + } + return NUM_SUITS; +} + +unsigned get_largest_element(unsigned *arr, size_t n) { + if (n == 0) { + return 0; + } + unsigned ans = arr[0]; + for (size_t i = 1; i < n; i++) { + if (arr[i] > ans) { + ans = arr[i]; + } + } + return ans; +} + +size_t get_match_index(unsigned *match_counts, size_t n, unsigned n_of_akind) { + + for (size_t i = 0; i < n; i++) { + if (match_counts[i] == n_of_akind) { + return i; + } + } + assert(1 == 2); +} + +ssize_t find_secondary_pair(deck_t *hand, unsigned *match_counts, + size_t match_idx) { + + for (size_t i = 0; i < hand->n_cards; i++) { + if (match_counts[i] > 1) { + if (hand->cards[i]->value != hand->cards[match_idx]->value) { + return i; + } + } + } + return -1; +} + +int is_ace_low_straight_at_five(deck_t *hand, size_t index, suit_t fs) { + int straightTally = 2; + int currentCardValue = 5; + for (size_t i = index + 1; i < hand->n_cards; i++) { + if (straightTally == 5) { + return -1; + } + + if (hand->cards[i]->value == currentCardValue) { + continue; + } + + if (hand->cards[i]->value == currentCardValue - 1) { + if (fs == NUM_SUITS || hand->cards[i]->suit == fs) { + straightTally++; + currentCardValue--; + } else { + continue; + } + } else { + break; + } + } + if (straightTally == 5) { + return -1; + } + return 0; +} + +int is_ace_low_straight_at(deck_t *hand, size_t index, suit_t fs) { + int currentCardValue = hand->cards[index]->value; + if (currentCardValue == 5) { + return is_ace_low_straight_at_five(hand, index, fs); + } + + if (currentCardValue == VALUE_ACE) { + for (size_t i = index + 1; i < hand->n_cards; i++) { + + if (hand->cards[i]->value == currentCardValue) { + continue; + } + + if (hand->cards[i]->value == 5) { + return is_ace_low_straight_at_five(hand, i, fs); + } + } + } + + return 0; +} + +int is_straight_at(deck_t *hand, size_t index, suit_t fs) { + if (hand->cards[index]->suit != fs && fs != NUM_SUITS) { + return 0; + } + int currentCardValue = hand->cards[index]->value; + if (currentCardValue < 5) { + return 0; + } + if (currentCardValue == 5) { + if (hand->cards[0]->value != 14) { + return 0; + } else { + return is_ace_low_straight_at(hand, index, fs); + } + } + + int straightTally = 1; + for (size_t i = index + 1; i < hand->n_cards; i++) { + if (straightTally == 5) { + return 1; + } + + if (hand->cards[i]->value == currentCardValue) { + continue; + } + + if (hand->cards[i]->value == currentCardValue - 1) { + if (fs == NUM_SUITS || hand->cards[i]->suit == fs) { + straightTally++; + currentCardValue--; + } else { + continue; + } + } else { + break; + } + } + if (straightTally == 5) { + return 1; + } + + if (hand->cards[index]->value == VALUE_ACE) { + return is_ace_low_straight_at(hand, index, fs); + } else { + return 0; + } +} + +hand_eval_t build_hand_from_match(deck_t *hand, unsigned n, hand_ranking_t what, + size_t idx) { + + hand_eval_t ans; + ans.ranking = what; + for (int i = 0; i < n; i++) { + ans.cards[i] = hand->cards[idx + i]; + } + + if (idx == 0) { + for (int i = n; i < 5; i++) { + ans.cards[i] = hand->cards[i]; + } + } else if (idx >= 5 - n) { + for (int i = 0; i < 5 - n; i++) { + ans.cards[n + i] = hand->cards[i]; + } + } else { + for (int i = 0; i < idx; i++) { + ans.cards[n + i] = hand->cards[i]; + } + for (int i = idx + n; i < 5; i++) { + ans.cards[i] = hand->cards[i]; + } + } + return ans; +} + +int compare_hands(deck_t *hand1, deck_t *hand2) { + + qsort(hand1->cards, hand1->n_cards, sizeof(card_t), card_ptr_comp); + qsort(hand2->cards, hand2->n_cards, sizeof(card_t), card_ptr_comp); + hand_eval_t ht1 = evaluate_hand(hand1); + hand_eval_t ht2 = evaluate_hand(hand2); + + if (ht1.ranking != ht2.ranking) { + return ht1.ranking - ht2.ranking; + } + + for (int i = 0; i < 5; i++) { + if (ht1.cards[i]->value == ht2.cards[i]->value) { + continue; + } else { + return ht1.cards[i]->value - ht2.cards[i]->value; + } + } + return 0; +} + +// You will write this function in Course 4. +// For now, we leave a prototype (and provide our +// implementation in eval-c4.o) so that the +// other functions we have provided can make +// use of get_match_counts. +unsigned *get_match_counts(deck_t *hand); + +// We provide the below functions. You do NOT need to modify them +// In fact, you should not modify them! + +// This function copies a straight starting at index "ind" from deck "from". +// This copies "count" cards (typically 5). +// into the card array "to" +// if "fs" is NUM_SUITS, then suits are ignored. +// if "fs" is any other value, a straight flush (of that suit) is copied. +void copy_straight(card_t **to, deck_t *from, size_t ind, suit_t fs, + size_t count) { + assert(fs == NUM_SUITS || from->cards[ind]->suit == fs); + unsigned nextv = from->cards[ind]->value; + size_t to_ind = 0; + while (count > 0) { + assert(ind < from->n_cards); + assert(nextv >= 2); + assert(to_ind < 5); + if (from->cards[ind]->value == nextv && + (fs == NUM_SUITS || from->cards[ind]->suit == fs)) { + to[to_ind] = from->cards[ind]; + to_ind++; + count--; + nextv--; + } + ind++; + } +} + +// This looks for a straight (or straight flush if "fs" is not NUM_SUITS) +// in "hand". It calls the student's is_straight_at for each possible +// index to do the work of detecting the straight. +// If one is found, copy_straight is used to copy the cards into +// "ans". +int find_straight(deck_t *hand, suit_t fs, hand_eval_t *ans) { + if (hand->n_cards < 5) { + return 0; + } + for (size_t i = 0; i <= hand->n_cards - 5; i++) { + int x = is_straight_at(hand, i, fs); + if (x != 0) { + if (x < 0) { // ace low straight + assert(hand->cards[i]->value == VALUE_ACE && + (fs == NUM_SUITS || hand->cards[i]->suit == fs)); + ans->cards[4] = hand->cards[i]; + size_t cpind = i + 1; + while (hand->cards[cpind]->value != 5 || + !(fs == NUM_SUITS || hand->cards[cpind]->suit == fs)) { + cpind++; + assert(cpind < hand->n_cards); + } + copy_straight(ans->cards, hand, cpind, fs, 4); + } else { + copy_straight(ans->cards, hand, i, fs, 5); + } + return 1; + } + } + return 0; +} + +// This function puts all the hand evaluation logic together. +// This function is longer than we generally like to make functions, +// and is thus not so great for readability :( +hand_eval_t evaluate_hand(deck_t *hand) { + suit_t fs = flush_suit(hand); + hand_eval_t ans; + if (fs != NUM_SUITS) { + if (find_straight(hand, fs, &ans)) { + ans.ranking = STRAIGHT_FLUSH; + return ans; + } + } + unsigned *match_counts = get_match_counts(hand); + unsigned n_of_a_kind = get_largest_element(match_counts, hand->n_cards); + assert(n_of_a_kind <= 4); + size_t match_idx = get_match_index(match_counts, hand->n_cards, n_of_a_kind); + ssize_t other_pair_idx = find_secondary_pair(hand, match_counts, match_idx); + free(match_counts); + if (n_of_a_kind == 4) { // 4 of a kind + return build_hand_from_match(hand, 4, FOUR_OF_A_KIND, match_idx); + } else if (n_of_a_kind == 3 && other_pair_idx >= 0) { // full house + ans = build_hand_from_match(hand, 3, FULL_HOUSE, match_idx); + ans.cards[3] = hand->cards[other_pair_idx]; + ans.cards[4] = hand->cards[other_pair_idx + 1]; + return ans; + } else if (fs != NUM_SUITS) { // flush + ans.ranking = FLUSH; + size_t copy_idx = 0; + for (size_t i = 0; i < hand->n_cards; i++) { + if (hand->cards[i]->suit == fs) { + ans.cards[copy_idx] = hand->cards[i]; + copy_idx++; + if (copy_idx >= 5) { + break; + } + } + } + return ans; + } else if (find_straight(hand, NUM_SUITS, &ans)) { // straight + ans.ranking = STRAIGHT; + return ans; + } else if (n_of_a_kind == 3) { // 3 of a kind + return build_hand_from_match(hand, 3, THREE_OF_A_KIND, match_idx); + } else if (other_pair_idx >= 0) { // two pair + assert(n_of_a_kind == 2); + ans = build_hand_from_match(hand, 2, TWO_PAIR, match_idx); + ans.cards[2] = hand->cards[other_pair_idx]; + ans.cards[3] = hand->cards[other_pair_idx + 1]; + if (match_idx > 0) { + ans.cards[4] = hand->cards[0]; + } else if (other_pair_idx > 2) { // if match_idx is 0, first pair is in 01 + // if other_pair_idx > 2, then, e.g. A A K Q Q + ans.cards[4] = hand->cards[2]; + } else { // e.g., A A K K Q + ans.cards[4] = hand->cards[4]; + } + return ans; + } else if (n_of_a_kind == 2) { + return build_hand_from_match(hand, 2, PAIR, match_idx); + } + return build_hand_from_match(hand, 0, NOTHING, 0); +} diff --git a/c3prj2_eval/eval.h b/c3prj2_eval/eval.h new file mode 100644 index 0000000..1967917 --- /dev/null +++ b/c3prj2_eval/eval.h @@ -0,0 +1,13 @@ +#ifndef EVAL_H +#define EVAL_H +#include "deck.h" +struct hand_eval_tag { + hand_ranking_t ranking; + card_t *cards[5]; +}; +typedef struct hand_eval_tag hand_eval_t; + +hand_eval_t evaluate_hand(deck_t * hand) ; +int compare_hands(deck_t * hand1, deck_t * hand2) ; + +#endif diff --git a/c3prj2_eval/future.o b/c3prj2_eval/future.o new file mode 100755 index 0000000..a770c6a Binary files /dev/null and b/c3prj2_eval/future.o differ diff --git a/c3prj2_eval/grade.txt b/c3prj2_eval/grade.txt new file mode 100644 index 0000000..b751b74 --- /dev/null +++ b/c3prj2_eval/grade.txt @@ -0,0 +1,49 @@ +Grading at Sat 11 Dec 2021 09:52:46 PM UTC +Compiling your code +rm -f test poker cards.o my-test-main.o *~ +cc -ggdb3 -Wall -Werror -pedantic -std=gnu99 -c -o deck.o deck.c +cc -ggdb3 -Wall -Werror -pedantic -std=gnu99 -c -o eval.o eval.c +cc -ggdb3 -Wall -Werror -pedantic -std=gnu99 -c -o cards.o cards.c +gcc -o test-eval -ggdb3 deck.o deck-c4.o eval-c4.o eval.o test-eval.o cards.o input.o future.o +Testcase 1: Trying hands with nothing + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 2: Trying hands with pairs + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 3: Trying hands with 2 pairs + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 4: Trying hands with 3 of a kind + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 5: Trying hands with straights + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 6: Trying hands with flushes + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 7: Trying hands with full houses + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 8: Trying hands with 4 of a kind + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 9: Trying hands with straight flushes + Checking the output +Your file matched the expected output + - Testcase passed +Testcase 10: Trying each type of hand ranking + Checking the output +Your file matched the expected output + - Testcase passed + +Overall Grade: A diff --git a/c3prj2_eval/input.o b/c3prj2_eval/input.o new file mode 100755 index 0000000..65cc44b Binary files /dev/null and b/c3prj2_eval/input.o differ diff --git a/c3prj2_eval/t1.txt b/c3prj2_eval/t1.txt new file mode 100644 index 0000000..cc33372 --- /dev/null +++ b/c3prj2_eval/t1.txt @@ -0,0 +1,2 @@ +7c 3d 9h Qd As 2s; Kc 2h Js 6c 4d 7h + diff --git a/c3prj2_eval/t2.txt b/c3prj2_eval/t2.txt new file mode 100644 index 0000000..efc6951 --- /dev/null +++ b/c3prj2_eval/t2.txt @@ -0,0 +1,2 @@ +0s 9s 8c 7h 6d; Js 0c 9s 8d 7c + diff --git a/c3prj2_eval/t3.txt b/c3prj2_eval/t3.txt new file mode 100644 index 0000000..6620302 --- /dev/null +++ b/c3prj2_eval/t3.txt @@ -0,0 +1,2 @@ +As 5c 4h 3s 2c; 6c 5h 4s 3s 2d + diff --git a/c3prj2_eval/t4.txt b/c3prj2_eval/t4.txt new file mode 100644 index 0000000..3d961ef --- /dev/null +++ b/c3prj2_eval/t4.txt @@ -0,0 +1,2 @@ +Ac As Kc Ks Qs Js 0s; Ad Kh Qh Jh Js 0h 9h + diff --git a/c3prj2_eval/t5.txt b/c3prj2_eval/t5.txt new file mode 100644 index 0000000..d42cde9 --- /dev/null +++ b/c3prj2_eval/t5.txt @@ -0,0 +1,2 @@ +As Kc Qs Js 0s 2s; Kc Kd Kh Ks 8c 6d + diff --git a/c3prj2_eval/test-eval.o b/c3prj2_eval/test-eval.o new file mode 100644 index 0000000..92f6913 Binary files /dev/null and b/c3prj2_eval/test-eval.o differ diff --git a/c3prj2_eval/tests.txt b/c3prj2_eval/tests.txt new file mode 100755 index 0000000..63059d5 --- /dev/null +++ b/c3prj2_eval/tests.txt @@ -0,0 +1,30 @@ +Kc Qs Jd 9h 8c 7s 6d; Ac Qs Jd 9h 8c 7s 6d +Kh Ac Kc 8c 7h 3d 6s; Kh As 7h 3s 5d 6h Ks +As Ah Ks Kd 2h 3d 6s; Ac Ad Kh Kc Jd 3d 6s +Kh Kc Kd Ac 3s 6h 2s; Kh Kc Kd Ac 7h 6s 3d +Ks Qd Js 0h 6h 9d 2s; 0h 5d 8s 4s 3c 2h Ah +Ah Qh 5s 6d 9h 7h 2h; 4s 6s 5s 7d 0s Qs As +Kc Ks 3d 0c 0s Kh 8d; 9h Jd 9s Js 9d 3d 0c +0c 0s 0h 2d 7c 0d 2h; Kc Kd Kh Ks 3c 4h 5d +As 2d 3c Ks Qs Js 0s; 5s 4s 3s 2d Jc 2s As +As Ks 4d 5d Jd 0d Qd; Kd Qd Js 0s 9s 4s 5s +Qd Jd 0d 9s 9h 8h 8c; Jd 0d 9d 8s 8h 7h 7c +0c 0s 8s 7d 6d 6s 5c; Js 0s 8s 7d 6s 5c 4c +2d 3d 6d 8d 7c As Js; 2d 3d 6d 8d 7c Ah Ac +Ac 2d 3c 0s 4d 5s 0h; Ac 2d 3c 0s 4d 8h Jh +6c 3c 6d 4h Qs As 2d; 6c 3c 6d 4h Qs 2h Jh +6c 3c 6d 4h Qs 6h 2d; 6c 3c 6d 4h Qs 2h Jh +6c 3c 6d 4h 6h Qs 2d; 6c 3c 6d 4h Qs 6s Jh +6c 3c 6d 4h Qs 6h 2d; 6c 3c 6d 4h Qs 6s Ac +2d 3c 3d Jh Qs Js 3h; 2d 3c 3d Jh Qs Jd 3c +2d 3c 3d Jh Qs Js 2s; 2d 3c 3d Jh Qs Jd 3c +7d 2c 8h Ah 2h 9h 0h; 7d 2c 8h Ah 2h Jh Qh +6h 7h 8d 9d Jd 0d Qh; 6h 7h 8d 9d Jd 2c 9s +2c 5d 9h 8h 7h 3d As; 2c 5d 9h 8h 7h 0h Jh +Ah 2c 3c 7h 4s 5s 0d; Ah 2c 3c 7h 4s 0s 0h +4s 3s 8h 2s 9d As 5s; 4s 3s 8h 2s 9d 9h 9s +9d 9h 9s 8c 9c 0h Ah; 9d 9h 9s 8c 9c 0c As +9d 9h 8h 9c 7h 6h 5h; 9d 9h 8h 9c 7h 6c 5c +2s 3s 4s 6s 5h 8s 9h; As 2s 3s 4s 5s 8h 9h +2s 3s 4h 5s 6s 4s 0h; 2s 3s 4h 5s 6s 8d Kh +Kc Ac 2c 3c 2s Qc 7c; Kc Ac 2c As 2s 8c 5c -- cgit v1.2.3