diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..93d9f5c Binary files /dev/null and b/.DS_Store differ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..19a64d2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + + { + "name": "clang - Build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "lldb", + "preLaunchTask": "C/C++: clang build active file" + } + ] +} \ No newline at end of file diff --git a/.vscode/python-scene-game/game1_1.py b/.vscode/python-scene-game/game1_1.py new file mode 100644 index 0000000..ab81a1a --- /dev/null +++ b/.vscode/python-scene-game/game1_1.py @@ -0,0 +1,6 @@ +from scene import * + +class Game(Scene): + + def setup(self): + \ No newline at end of file diff --git a/docker.sh b/docker.sh new file mode 100644 index 0000000..43c8ec9 --- /dev/null +++ b/docker.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +#https://medium.com/@hudsonmendes/docker-have-a-ubuntu-development-machine-within-seconds-from-windows-or-mac-fd2f30a338e4 + +docker run \ + --name ubuntu \ + -e HOST_IP=$(ifconfig en0 | awk '/ *inet /{print $2}') \ + -v /Users/ngcchk/Downloads/_Github/solitaire-cli:/src \ + -t -i \ + ubuntu /bin/bash + + # already root no sudo + + #apt-get update + #apt-get install build-essential + #apt-get install libncurses5-dev libncursesw5-dev + + #setup UTF-8 still not ok + + # dpkg -l locales + +#apt-get install locales + + +#dpkg-reconfigure locales + +#apt-get install vim +#.bashrc b + +#export LC_ALL=en_US.UTF-8 +#export LANG=en_US.UTF-8 +#export LANGUAGE=en_US.UTF-8 + +#locale #to check + +#make ascii +#./solitaire + +#apt-get install gdb +# not sure about + +#solitaire-cli > c4c8 +Program received signal SIGSEGV, Segmentation fault. + 0x0000564565480953 in is_empty (pile=0x21) at main.c:198 +198 int is_empty(pile *pile) { return pile->num_cards == 0; } +(gdb) \ No newline at end of file diff --git a/highScore.txt b/highScore.txt new file mode 100644 index 0000000..cdb660b --- /dev/null +++ b/highScore.txt @@ -0,0 +1 @@ +700 \ No newline at end of file diff --git a/linux/.bashrc b/linux/.bashrc new file mode 100644 index 0000000..ba397cd --- /dev/null +++ b/linux/.bashrc @@ -0,0 +1,4 @@ +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 +export LANGUAGE=en_US.UTF-8 + diff --git a/linux/Makefile b/linux/Makefile new file mode 100644 index 0000000..f3a771d --- /dev/null +++ b/linux/Makefile @@ -0,0 +1,10 @@ +CC=cc +sol: + $(CC) -o solitaire -DUNICODE -Wall main.c -lncursesw -ltinfo +debug: + $(CC) -g -o solitaire -DUNICODE -Wall main.c -lncursesw -ltinfo +dascii: + $(CC) -g -o solitaire -Wall main.c -lncursesw -ltinfo +ascii: + $(CC) -o solitaire -Wall main.c -lncursesw -ltinfo + diff --git a/linux/core b/linux/core new file mode 100644 index 0000000..0c6cf84 Binary files /dev/null and b/linux/core differ diff --git a/linux/core.1 b/linux/core.1 new file mode 100644 index 0000000..417298d Binary files /dev/null and b/linux/core.1 differ diff --git a/linux/core.2 b/linux/core.2 new file mode 100644 index 0000000..7b4e4d7 Binary files /dev/null and b/linux/core.2 differ diff --git a/linux/main.c b/linux/main.c new file mode 100644 index 0000000..c4ad2a6 --- /dev/null +++ b/linux/main.c @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { + + printf("200: pile is %p", pile); // kwccoin insert a print to check the pile value before coredump + return pile->num_cards == 0; } // kwccoin: dump here + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; // kwccoin: orignal i.e. f5 is invlaid as it reachs to c but it will be invlaid up to f11 but problem with f12 +// char pile_types[] = "dwffffccccccccc"; // kwccoin: may test f5 is invlaid as it reachs to c but it will be invlaid up to f11 but problem with f12 + +// even extend 2 still same issue with c8 and f12 ... to be investigated + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Score: %d", state->score); + move(rows - 1, 0); + printw("solitaire-cli > "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; + char *pattern_single_move = "c%d %c%d"; + char *pattern_single_move2 = "%d %d"; + char *pattern_waste_move = "w %c%d"; + char *pattern_multi_stock = "%ds"; + char *pattern_stock = "s"; + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination"}; + +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + + if (destination_pile == (void *)0x21){ + return MOVE_INVALID_DESTINATION; + } + + + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + // kwccoin: not sure it is even a NULL Pointer + + printf("758: destination_pile is %p", destination_pile); // kwccoin insert a print to check the pile value before coredump + + + if (destination_pile == (void *)0x21){ + return MOVE_INVALID_DESTINATION; + } + + if (is_empty(destination_pile)) { // kwccoin: problem should be here + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + print_all_curses(state); + } + getch(); + end_curses(); +} diff --git a/linux/solitaire b/linux/solitaire new file mode 100755 index 0000000..978ae58 Binary files /dev/null and b/linux/solitaire differ diff --git a/test.c b/linux/test.c similarity index 100% rename from test.c rename to linux/test.c diff --git a/mac/.DS_Store b/mac/.DS_Store new file mode 100644 index 0000000..f20916c Binary files /dev/null and b/mac/.DS_Store differ diff --git a/mac/Makefile b/mac/Makefile new file mode 100644 index 0000000..b91ee8b --- /dev/null +++ b/mac/Makefile @@ -0,0 +1,8 @@ +CC=cc +sol: + $(CC) -o solitaire -DUNICODE -Wall main.c -lncurses +debug: + $(CC) -g -o solitaire -DUNICODE -Wall main.c -lncurses +ascii: + $(CC) -o solitaire -Wall main.c -lncurses + diff --git a/mac/a.out b/mac/a.out new file mode 100755 index 0000000..bf5bd58 Binary files /dev/null and b/mac/a.out differ diff --git a/mac/a.out.dSYM/Contents/Info.plist b/mac/a.out.dSYM/Contents/Info.plist new file mode 100644 index 0000000..3679a65 --- /dev/null +++ b/mac/a.out.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.a.out + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/mac/a.out.dSYM/Contents/Resources/DWARF/a.out b/mac/a.out.dSYM/Contents/Resources/DWARF/a.out new file mode 100644 index 0000000..0547c81 Binary files /dev/null and b/mac/a.out.dSYM/Contents/Resources/DWARF/a.out differ diff --git a/mac/main-highscore.c.txt b/mac/main-highscore.c.txt new file mode 100644 index 0000000..f6a64c6 --- /dev/null +++ b/mac/main-highscore.c.txt @@ -0,0 +1,822 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Score: %d", state->score); + move(rows - 1, 0); + printw("solitaire-cli > "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //#c#c# + char *pattern_single_move = "c%d %c%d"; //c#c# + char *pattern_single_move2 = "%d %d"; //## + char *pattern_waste_move = "w %c%d"; //wf# or wc# + char *pattern_multi_stock = "%ds"; //#s + char *pattern_stock = "s"; //s + char *pattern_help = "h"; //h + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#"}; +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + //catch source / destination too high + if((parsed.destination == 'c' && ((parsed.destination_index >= COLUMN_COUNT + 1) || (parsed.destination_index < 1))) + || (parsed.destination == 'f' && ((parsed.destination_index >= FOUNDATION_COUNT + 1) || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + // source_index can also be broken + if(parsed.source == 'c' && ((parsed.source_index >= COLUMN_COUNT + 1) || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + print_all_curses(state); + } + getch(); + end_curses(); +} diff --git a/mac/main-old-mac.c b/mac/main-old-mac.c new file mode 100644 index 0000000..c4ad2a6 --- /dev/null +++ b/mac/main-old-mac.c @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { + + printf("200: pile is %p", pile); // kwccoin insert a print to check the pile value before coredump + return pile->num_cards == 0; } // kwccoin: dump here + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; // kwccoin: orignal i.e. f5 is invlaid as it reachs to c but it will be invlaid up to f11 but problem with f12 +// char pile_types[] = "dwffffccccccccc"; // kwccoin: may test f5 is invlaid as it reachs to c but it will be invlaid up to f11 but problem with f12 + +// even extend 2 still same issue with c8 and f12 ... to be investigated + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Score: %d", state->score); + move(rows - 1, 0); + printw("solitaire-cli > "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; + char *pattern_single_move = "c%d %c%d"; + char *pattern_single_move2 = "%d %d"; + char *pattern_waste_move = "w %c%d"; + char *pattern_multi_stock = "%ds"; + char *pattern_stock = "s"; + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination"}; + +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + + if (destination_pile == (void *)0x21){ + return MOVE_INVALID_DESTINATION; + } + + + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + // kwccoin: not sure it is even a NULL Pointer + + printf("758: destination_pile is %p", destination_pile); // kwccoin insert a print to check the pile value before coredump + + + if (destination_pile == (void *)0x21){ + return MOVE_INVALID_DESTINATION; + } + + if (is_empty(destination_pile)) { // kwccoin: problem should be here + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + print_all_curses(state); + } + getch(); + end_curses(); +} diff --git a/mac/main-original.c b/mac/main-original.c new file mode 100644 index 0000000..dfb940b --- /dev/null +++ b/mac/main-original.c @@ -0,0 +1,952 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +int HIGH_SCORE; + + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "~H"; + case SUIT_SPADE: + return "~S"; + case SUIT_CLUB: + return "-c"; + case SUIT_DIAMOND: + return "-d"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Current Score: %d (Last High Score is %d)", state->score, HIGH_SCORE); + move(rows - 1, 0); + printw("solitaire-cli (h for help)> "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //kwc: #c#c# + char *pattern_single_move = "c%d %c%d"; //kwc: c#c# + char *pattern_single_move2 = "%d %d"; //kwc: ## + char *pattern_waste_move = "w %c%d"; //kwc: wf# or wc# + char *pattern_multi_stock = "%ds"; //kwc: #s + char *pattern_stock = "s"; //kwc: s + char *pattern_help = "h"; //kwc: h + char *pattern_quit = "quit"; //kwc: quit + char *pattern_refresh = "r"; //kwc: refresh + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else if (strcmp(command, pattern_refresh) == 0) { + parsed.source = 'r'; + } else if (strcmp(command, pattern_quit) == 0) { + parsed.source = 'q'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP, + MOVE_REFRESH, + MOVE_QUIT +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#, r, quit", + "refresh", + "quit"}; + +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + + /* kwc: checking why valgrind said there are uninitated : + + printf("parsed: %c %c %i %i %i %i $i\t\t", + parsed.source, + parsed.destination, + parsed.source_index, + parsed.destination_index, + parsed.source_amount, + parsed.success) ; */ + + /* rsed: s 48 28 1 1 $i + parsed: r 48 28 1 1 $i parsed: s 48 28 1 1 $i + parsed: w c 48 5 1 1 $i parsed: w f 48 4 1 1 $i + parsed: c f 4 4 1 1 $i parsed: w f 48 4 1 1 $i + parsed: c f 7 4 1 1 $i parsed: c f 7 2 1 1 $i + parsed: c c 4 7 5 1 $i parsed: c c 6 4 1 1 $i + parsed: c c 6 3 1 1 $i parsed: c f 6 1 1 1 $i + parsed: c f 6 3 1 1 $i parsed: c f 6 4 1 1 $i + parsed: c f 7 1 1 1 $i parsed: s 48 28 1 1 $i + parsed: w f 48 2 1 1 $i parsed: c f 7 2 1 1 $i + parsed: s 48 28 1 1 $i parsed: w f 48 1 1 1 $i + parsed: r 48 28 1 1 $i parsed: q 48 28 1 1 $i + high score is 210 + + command like s has no destination and + w has no source + r (mine) has no either + + But only the s has issues as one of the s command check destination + + */ + + +/*#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +//#endif */ + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + if(parsed.source == 'r' ) + { + return MOVE_REFRESH; + } + + //catch source is quit - just display message for the moment + if(parsed.source == 'q' ) + { + return MOVE_QUIT; + } + + // kwc: move some sources here seems to solve the problem of valgrind unitiated alert + // source_index can also be broken + + if(parsed.source == 'c' + && ((parsed.source_index >= COLUMN_COUNT + 1) + || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + //catch source / destination too high // kwc: these should last to avoid unitiated + if( + (parsed.destination == 'c' // kwc: unitiated value detected + + && ((parsed.destination_index >= COLUMN_COUNT + 1) + + || (parsed.destination_index < 1))) + || + (parsed.destination == 'f' // kwc: unitiated value detected + + && ((parsed.destination_index >= FOUNDATION_COUNT + 1) + + || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + + FILE *fptr; + + fptr = fopen("highScore.txt","r"); + + if(fptr == NULL){ + HIGH_SCORE =0; + } else { + fscanf(fptr, "%i",&HIGH_SCORE); + fclose(fptr); + } + + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + if (result == MOVE_REFRESH){ + erase(); + refresh(); + print_all_curses(state); + } else if (result == MOVE_QUIT) { + // add these seems cannot save the command line + // print_all_curses(state); + //end_curses(); + //initscr(); // visual mode, no need if not + //erase() + refresh(); + getch(); + endwin(); + printf("high score is %d\n", state->score); // may save and launch with the overall high score ??? + fptr = fopen("highScore.txt","w"); + if(fptr == NULL){ + printf("Error write HIGH_SCORE"); + exit(1); + } else { + if (state->score < HIGH_SCORE){ + state->score = HIGH_SCORE; + } + fprintf(fptr, "%i",state->score); + } + fclose(fptr); + exit(0); + } else { + print_all_curses(state); + } + } + getch(); // not sure about these 2 lines + end_curses(); // add this but still not ok; the command line broken +} diff --git a/mac/main.c b/mac/main.c new file mode 100644 index 0000000..f6a64c6 --- /dev/null +++ b/mac/main.c @@ -0,0 +1,822 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Score: %d", state->score); + move(rows - 1, 0); + printw("solitaire-cli > "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //#c#c# + char *pattern_single_move = "c%d %c%d"; //c#c# + char *pattern_single_move2 = "%d %d"; //## + char *pattern_waste_move = "w %c%d"; //wf# or wc# + char *pattern_multi_stock = "%ds"; //#s + char *pattern_stock = "s"; //s + char *pattern_help = "h"; //h + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#"}; +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + //catch source / destination too high + if((parsed.destination == 'c' && ((parsed.destination_index >= COLUMN_COUNT + 1) || (parsed.destination_index < 1))) + || (parsed.destination == 'f' && ((parsed.destination_index >= FOUNDATION_COUNT + 1) || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + // source_index can also be broken + if(parsed.source == 'c' && ((parsed.source_index >= COLUMN_COUNT + 1) || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + print_all_curses(state); + } + getch(); + end_curses(); +} diff --git a/mac/solitaire b/mac/solitaire new file mode 100755 index 0000000..93f1d5c Binary files /dev/null and b/mac/solitaire differ diff --git a/mac/solitaire.dSYM/Contents/Info.plist b/mac/solitaire.dSYM/Contents/Info.plist new file mode 100644 index 0000000..f38ed14 --- /dev/null +++ b/mac/solitaire.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.solitaire + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/mac/solitaire.dSYM/Contents/Resources/DWARF/solitaire b/mac/solitaire.dSYM/Contents/Resources/DWARF/solitaire new file mode 100644 index 0000000..1b6a31e Binary files /dev/null and b/mac/solitaire.dSYM/Contents/Resources/DWARF/solitaire differ diff --git a/mac/test.c b/mac/test.c new file mode 100644 index 0000000..923f56b --- /dev/null +++ b/mac/test.c @@ -0,0 +1,92 @@ + +void test_cards() { + card c5H = make_card(SUIT_HEART, RANK_5); + card c5S = make_card(SUIT_SPADE, RANK_5); + card c6H = make_card(SUIT_HEART, RANK_6); + card c6D = make_card(SUIT_DIAMOND, RANK_6); + card c5D = make_card(SUIT_DIAMOND, RANK_5); + card c7D = make_card(SUIT_DIAMOND, RANK_7); + printf("5s is black %d vs 1 \n", is_black(c5S)); + printf("5s is red %d vs 0 \n", is_red(c5S)); + printf("6h is red %d vs 1 \n", is_red(c6H)); + printf("6h is black %d vs 0 \n", is_black(c6H)); + printf("5h 6h is alternate %d vs 0 \n", is_alternate_color(c5H, c6H)); + printf("5s 6h is alternate %d vs 1 \n", is_alternate_color(c5S, c6H)); + printf("5d 6d is same suit %d vs 1 \n", is_same_suit(c5D, c6D)); + printf("6h 6d is same suit %d vs 0 \n", is_same_suit(c6H, c6D)); + printf("5s 6h in sequence %d vs 1 \n", is_in_sequence(c5S, c6H)); + printf("6h 6d in sequence %d vs 0 \n", is_in_sequence(c6H, c6D)); + printf("5s 7d in sequence %d vs 0 \n", is_in_sequence(c5S, c7D)); + printf("5h 6h can be placed on foundation %d vs 1\n", + can_be_placed_on_foundation(c5H, c6H)); + printf("5h 6h can be placed on foundation %d vs 0\n", + can_be_placed_on_foundation(c6H, c5H)); + printf("6h 5s can be placed on the bottom %d vs 1\n", + can_be_placed_bottom(c6H, c5S)); + printf("5h 6h can be placed on the bottom %d vs 0\n", + can_be_placed_bottom(c5H, c6H)); + printf("6h 5h can be placed on the bottom %d vs 0\n", + can_be_placed_bottom(c6H, c5H)); + // TODO can_be_placed_on_bottom test +} + + +void test_pile_operations() { + pile *initial_deck = make_pile(); + fill_deck(initial_deck); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npop\n"); + card_ptr popped_card = pop(initial_deck); + PRINTCARD(popped_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npop\n"); + card_ptr popped_card_2 = pop(initial_deck); + PRINTCARD(popped_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nshift\n"); + card_ptr dequeued_card = shift(initial_deck); + PRINTCARD(dequeued_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nshift\n"); + card_ptr dequeued_card_2 = shift(initial_deck); + PRINTCARD(dequeued_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nunshift 2H\n"); + unshift(initial_deck, dequeued_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nunshift 3H\n"); + unshift(initial_deck, dequeued_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npush 4H\n"); + push(initial_deck, popped_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); +} + +void print_pile_ptrs(pile *pile) { + printf("@["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" @ %p | ", c); + + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + diff --git a/myTest/Makefile b/myTest/Makefile new file mode 100644 index 0000000..5f99bc8 --- /dev/null +++ b/myTest/Makefile @@ -0,0 +1,15 @@ +# http://web.cse.ohio-state.edu/~reeves.92/CSE2421au12/SlidesDay22.pdf +CC=gcc + +all: passR + +passR: passR.o loadLinkList.o + $(CC) -g -o passR loadLinkList.o passR.o -DUNICODE -Wall -lncursesw -ltinfo +loadLinkList.o : loadLinkList.c + $(CC) -c loadLinkList.c +passR.o : passR.c + $(CC) -c passR.c +clean: + rm -rf passR *.o +run: + ./passR diff --git a/myTest/Makefile-no-good.txt b/myTest/Makefile-no-good.txt new file mode 100644 index 0000000..67d378c --- /dev/null +++ b/myTest/Makefile-no-good.txt @@ -0,0 +1,11 @@ +CC=cc +passR: passR.c loadLinkList.c + $(CC) -g -o passR loadLinkList.o main.o -DUNICODE -Wall passR.c -lncursesw -ltinfo +nodebug: passR.c + $(CC) -o passR -DUNICODE -Wall passR.c -lncursesw -ltinfo +nodascii: passR.c + $(CC) -o passR -Wall passR.c -lncursesw -ltinfo +ascii: passR.c + $(CC) -g -o passR -Wall passR.c -lncursesw -ltinfo +clean: + rm -f passR diff --git a/myTest/Makefile-still-no-good.txt b/myTest/Makefile-still-no-good.txt new file mode 100644 index 0000000..8ac342b --- /dev/null +++ b/myTest/Makefile-still-no-good.txt @@ -0,0 +1,12 @@ +CC = gcc +CFLAGS = -Wall -g +DEPS = loadLinkList.h +LDFLAGS = +OBJFILES = passR.o loadLinkList.o + +%.o: %.c $(DEPS) + $(CC) $(CFLAGS) -c -o $@ $< +passR: $(OBJ) + gcc $(CFLAGS) -o $@ $^ +clean: + rm -f $(OBJFILES) $(TARGET) *~ \ No newline at end of file diff --git a/myTest/core b/myTest/core new file mode 100644 index 0000000..de5ae92 Binary files /dev/null and b/myTest/core differ diff --git a/myTest/factorial.c b/myTest/factorial.c new file mode 100644 index 0000000..ed58563 --- /dev/null +++ b/myTest/factorial.c @@ -0,0 +1,13 @@ +# include + +int main() +{ + int i, num, j; + printf ("Enter the number: "); + scanf ("%d", &num ); + + for (i=1; iaChar = oneChar; + (* headLinkList).aChar = oneChar; + headLinkList->anInt = oneInt; + headLinkList->aLinkList = tailLinkList; + + // (* x). same as x-> + +} \ No newline at end of file diff --git a/myTest/loadLinkList.h b/myTest/loadLinkList.h new file mode 100644 index 0000000..819ef18 --- /dev/null +++ b/myTest/loadLinkList.h @@ -0,0 +1,15 @@ +#ifndef LOADLINKLIST_H +#define LOADLINKLIST_H +struct linkList { + char aChar; + int anInt; + struct linkList * aLinkList; +}; + +void loadLinkList( + struct linkList* headLinkList, + char oneChar, + int oneInt, + struct linkList* tailLinkList +); +#endif \ No newline at end of file diff --git a/myTest/loadLinkList.o b/myTest/loadLinkList.o new file mode 100644 index 0000000..d6ad928 Binary files /dev/null and b/myTest/loadLinkList.o differ diff --git a/myTest/passR b/myTest/passR new file mode 100755 index 0000000..8f6304f Binary files /dev/null and b/myTest/passR differ diff --git a/myTest/passR.c b/myTest/passR.c new file mode 100644 index 0000000..876c74f --- /dev/null +++ b/myTest/passR.c @@ -0,0 +1,293 @@ +// more ARM but hardware oriented exercise: +// https://bob.cs.sonoma.edu/IntroCompOrg-RPi/sec-array.html + +// pass-reference.c -- show pass by reference using pointers +/* compile with: +cc -g -Wall -o pass-reference pass-reference.c */ + +#include // for printf() +#include "loadLinkList.h" + +int arrayAdd1(int n) +{ + int intArray[n]; + // register int index; + + for (int index = 0; index < n; index++) + intArray[index] = index; + + int sum = 0; + + for (int index = 0; index < n; index++){ + printf("%i, ", intArray[index]); + sum = sum + intArray[index]; + } + + printf("\n"); + + return sum; +} + +int arrayAdd2(int m, int intArray[]) +{ + int sum = 0; + + for (int index = 0; index < m; index++){ + printf("%i, ", intArray[index]); + sum = sum + intArray[index]; + } + + printf("\n"); + + return sum; +} + +int addemUp (int a, int b, int *result) { + *result = a + b; // not using functional approach or return (result) + return *result; // not result as it is a pointer nor *result but definitely not & result +} // addemUp + +void printLinkList(struct linkList* inLinkList){ + printf("pLL: structure list is %p, %c, %i, %p\n", + inLinkList, + inLinkList->aChar, + inLinkList->anInt, + inLinkList->aLinkList); +} + +int main (int argc, char *argv[]) { + int answer1, answer2; + answer1=2; + answer2=256*256; + answer2 = addemUp (2, 256*256, &answer1); + printf ("1 + 256*256 = %d or %d\n", answer1, answer2); + int n = 10; + printf ("Add up to %i = %i\n", n, arrayAdd1(n)); + int m = 10; + int intArray[m]; // beware this is NOT dynamic allocation + for (int index = 0; index < m; index++) + intArray[index] = index; + printf ("Add up to %i = %i\n", m, arrayAdd2(m, intArray)); + printf ("Add up to %i = %i\n", m, arrayAdd2(m, &intArray[0])); + // this is the danger or c array with implicit pointer reference + // first it use poiniter ref without telling you + // and when you use pointer ref it is not obvious + // you cannot use &intArray as it would be "int (*)[10]" + // what expect is "int *" + + struct linkList LL1; + struct linkList LL2; + struct linkList LL3; + + loadLinkList(&LL1, 'a', 1, &LL3); + loadLinkList(&LL2, 'b', 2, &LL1); + loadLinkList(&LL3, 'c', 3, &LL2); + + //struct linkList LL4 = {&LL4, 'd', 4, &LL3}; // not working except for simple structure + //struct linkList LL5; + //LL5 = (LinkList) {&LL4, 'd', 4, &LL3}; + + // pointer can be %p, %u, %x C99 use p + // %i/%d + // + + printf("LL1: structure list is %p, %c, %i, %p\n", &LL1, LL1.aChar, LL1.anInt, LL1.aLinkList); + printf("LL2: structure list is %p, %c, %i, %p\n", &LL2, LL2.aChar, LL2.anInt, LL2.aLinkList); + printf("LL3: structure list is %p, %c, %i, %p\n", &LL3, LL3.aChar, LL3.anInt, LL3.aLinkList); + //printf("LL4: structure list is %p, %c, %i, %p\n", &LL4, LL4.aChar, LL4.anInt, LL4.aLinkList); + + // LL1/2/3 is a variable of "type" linkList and use . + // in the function you use pointer -> because the inLinkList + // pointer "type" and is a pointer to structure, + + printLinkList(&LL1); + printLinkList(&LL2); + printLinkList(&LL3); + //printLinkList(&LL4); + + /* // core dump + long *lp = (void *)0x10000000; + for (int i = 0; i < 5; i++) { + printf ("%d: %lx\n", i, *lp); + lp++; } + */ + + return (0); +} // main + +/* + +For help, type "help". +Type "apropos word" to search for commands related to "word"... +Reading symbols from solitaire... +(gdb) linenumber +Undefined command: "linenumber". Try "help". +(gdb) linenumber on +Undefined command: "linenumber". Try "help". +(gdb) help +List of classes of commands: + +aliases -- Aliases of other commands. +breakpoints -- Making program stop at certain points. +data -- Examining data. +files -- Specifying and examining files. +internals -- Maintenance commands. +obscure -- Obscure features. +running -- Running the program. +stack -- Examining the stack. +status -- Status inquiries. +support -- Support facilities. +tracepoints -- Tracing of program execution without stopping the program. +user-defined -- User-defined commands. + +Type "help" followed by a class name for a list of commands in that class. +Type "help all" for the list of all commands. +Type "help" followed by command name for full documentation. +Type "apropos word" to search for commands related to "word". +Type "apropos -v word" for full documentation of commands related to "word". +Command name abbreviations are allowed if unambiguous. +(gdb) ehlp all +Undefined command: "ehlp". Try "help". +(gdb) breakpoints 10 +Undefined command: "breakpoints". Try "help". +(gdb) breakpoint 10 +Undefined command: "breakpoint". Try "help". +(gdb) run +Starting program: /src/linux/solitaire +warning: Error disabling address space randomization: Operation not permitted + +(gdb) b 15 +Breakpoint 2 at 0x11cc: file passR.c, line 15. +(gdb) r +Starting program: /src/myTest/passR +warning: Error disabling address space randomization: Operation not permitted + +Breakpoint 1, main (argc=1, argv=0x7ffc3393cba8) at passR.c:14 +14 answer2 = addemUp (2, 256, &answer1); +(gdb) n + +Breakpoint 2, main (argc=1, argv=0x7ffc3393cba8) at passR.c:15 +15 printf ("1 + 256 = %d or %d\n", answer1, answer2); +(gdb) i r +rax 0x102 258 +rbx 0x56273184f200 94726334509568 +rcx 0x56273184f200 94726334509568 +rdx 0x102 258 +rsi 0x100 256 +rdi 0x2 2 +rbp 0x7ffc3393cab0 0x7ffc3393cab0 +rsp 0x7ffc3393ca90 0x7ffc3393ca90 +r8 0x0 0 +r9 0x7f53f20c2d50 139998519897424 +r10 0x0 0 +r11 0x0 0 +r12 0x56273184f080 94726334509184 +r13 0x7ffc3393cba0 140721173810080 +r14 0x0 0 +r15 0x0 0 +rip 0x56273184f1cc 0x56273184f1cc +eflags 0x202 [ IF ] +cs 0x33 51 +ss 0x2b 43 +ds 0x0 0 +es 0x0 0 +fs 0x0 0 +gs 0x0 0 +(gdb) x /4xb &answer1 +0x7ffc3393caa0: 0x02 0x01 0x00 0x00 +(gdb) x /4xb &answer2 +0x7ffc3393caa4: 0x02 0x01 0x00 0x00 +(gdb) x /4xw &answer2 +0x7ffc3393caa4: 0x00000102 0x5ce67100 0xee4391f9 0x00000000 +(gdb) p &answer1 +$1 = (int *) 0x7ffc3393caa0 +(gdb) p answer1 +$2 = 258 +(gdb) q + +*/ + +/* + +cc -g -o passR -DUNICODE -Wall passR.c -lncursesw -ltinfo +root@df550a16eb2d:/src/myTest# gdb ./passR +GNU gdb (Ubuntu 9.1-0ubuntu1) 9.1 +Copyright (C) 2020 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +Type "show copying" and "show warranty" for details. +This GDB was configured as "x86_64-linux-gnu". +Type "show configuration" for configuration details. +For bug reporting instructions, please see: +. +Find the GDB manual and other documentation resources online at: + . + +For help, type "help". +Type "apropos word" to search for commands related to "word"... +Reading symbols from ./passR... +(gdb) b 16 +Breakpoint 1 at 0x11c1: file passR.c, line 16. +(gdb) b17 +Undefined command: "b17". Try "help". +(gdb) b 17 +Breakpoint 2 at 0x11da: file passR.c, line 17. +(gdb) r +Starting program: /src/myTest/passR +warning: Error disabling address space randomization: Operation not permitted + +Breakpoint 1, main (argc=1, argv=0x7ffc824f8c78) at passR.c:16 +16 answer2 = addemUp (2, 256*256, &answer1); +(gdb) x /4xw &answer1 +0x7ffc824f8b70: 0x00000002 0x00010000 0x86214000 0xb144a4b1 +(gdb) p answer1 +$1 = 2 +(gdb) p &answer1 +$2 = (int *) 0x7ffc824f8b70 +(gdb) p *answer1 +Cannot access memory at address 0x2 +(gdb) n + +Breakpoint 2, main (argc=1, argv=0x7ffc824f8c78) at passR.c:17 +17 printf ("1 + 256*256 = %d or %d\n", answer1, answer2); +(gdb) x /4xw &answer1 +0x7ffc824f8b70: 0x00010002 0x00010002 0x86214000 0xb144a4b1 +(gdb) q +*/ + +/* // https://stackoverflow.com/questions/8019615/strings-and-character-with-printf + +#include + +void main() +{ + char name[]="siva"; + printf("name = %p\n", name); + printf("&name[0] = %p\n", &name[0]); + printf("name printed as %%s is %s\n",name); + printf("*name = %c\n",*name); + printf("name[0] = %c\n", name[0]); +} +Output is: + +name = 0xbff5391b +&name[0] = 0xbff5391b +name printed as %s is siva +*name = s +name[0] = s +So 'name' is actually a pointer to the array of characters in memory. If you try reading the first four bytes at 0xbff5391b, you will see 's', 'i', 'v' and 'a' + +Location Data +========= ====== + +0xbff5391b 0x73 's' ---> name[0] +0xbff5391c 0x69 'i' ---> name[1] +0xbff5391d 0x76 'v' ---> name[2] +0xbff5391e 0x61 'a' ---> name[3] +0xbff5391f 0x00 '\0' ---> This is the NULL termination of the string +To print a character you need to pass the value of the character to printf. The value can be referenced as name[0] or *name (since for an array name = &name[0]). + +To print a string you need to pass a pointer to the string to printf (in this case 'name' or '&name[0]'). + +*/ \ No newline at end of file diff --git a/myTest/passR.o b/myTest/passR.o new file mode 100644 index 0000000..634b612 Binary files /dev/null and b/myTest/passR.o differ diff --git a/solitaire b/solitaire new file mode 100755 index 0000000..170fb70 Binary files /dev/null and b/solitaire differ diff --git a/solitaire-cli-master-fixed-unchanged/LICENSE b/solitaire-cli-master-fixed-unchanged/LICENSE new file mode 100644 index 0000000..3d7786e --- /dev/null +++ b/solitaire-cli-master-fixed-unchanged/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Juraj Borza + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/solitaire-cli-master-fixed-unchanged/Makefile similarity index 76% rename from Makefile rename to solitaire-cli-master-fixed-unchanged/Makefile index 8f3fe52..9f25d08 100644 --- a/Makefile +++ b/solitaire-cli-master-fixed-unchanged/Makefile @@ -5,4 +5,5 @@ debug: $(CC) -g -o solitaire -DUNICODE -Wall main.c -lncursesw -ltinfo ascii: $(CC) -o solitaire -Wall main.c -lncursesw -ltinfo - +mac: + $(CC) -g -o solitaire -DUNICODE -Wall main.c -lncurses diff --git a/main.c b/solitaire-cli-master-fixed-unchanged/main.c similarity index 100% rename from main.c rename to solitaire-cli-master-fixed-unchanged/main.c diff --git a/solitaire-cli-master-fixed-unchanged/test.c b/solitaire-cli-master-fixed-unchanged/test.c new file mode 100644 index 0000000..923f56b --- /dev/null +++ b/solitaire-cli-master-fixed-unchanged/test.c @@ -0,0 +1,92 @@ + +void test_cards() { + card c5H = make_card(SUIT_HEART, RANK_5); + card c5S = make_card(SUIT_SPADE, RANK_5); + card c6H = make_card(SUIT_HEART, RANK_6); + card c6D = make_card(SUIT_DIAMOND, RANK_6); + card c5D = make_card(SUIT_DIAMOND, RANK_5); + card c7D = make_card(SUIT_DIAMOND, RANK_7); + printf("5s is black %d vs 1 \n", is_black(c5S)); + printf("5s is red %d vs 0 \n", is_red(c5S)); + printf("6h is red %d vs 1 \n", is_red(c6H)); + printf("6h is black %d vs 0 \n", is_black(c6H)); + printf("5h 6h is alternate %d vs 0 \n", is_alternate_color(c5H, c6H)); + printf("5s 6h is alternate %d vs 1 \n", is_alternate_color(c5S, c6H)); + printf("5d 6d is same suit %d vs 1 \n", is_same_suit(c5D, c6D)); + printf("6h 6d is same suit %d vs 0 \n", is_same_suit(c6H, c6D)); + printf("5s 6h in sequence %d vs 1 \n", is_in_sequence(c5S, c6H)); + printf("6h 6d in sequence %d vs 0 \n", is_in_sequence(c6H, c6D)); + printf("5s 7d in sequence %d vs 0 \n", is_in_sequence(c5S, c7D)); + printf("5h 6h can be placed on foundation %d vs 1\n", + can_be_placed_on_foundation(c5H, c6H)); + printf("5h 6h can be placed on foundation %d vs 0\n", + can_be_placed_on_foundation(c6H, c5H)); + printf("6h 5s can be placed on the bottom %d vs 1\n", + can_be_placed_bottom(c6H, c5S)); + printf("5h 6h can be placed on the bottom %d vs 0\n", + can_be_placed_bottom(c5H, c6H)); + printf("6h 5h can be placed on the bottom %d vs 0\n", + can_be_placed_bottom(c6H, c5H)); + // TODO can_be_placed_on_bottom test +} + + +void test_pile_operations() { + pile *initial_deck = make_pile(); + fill_deck(initial_deck); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npop\n"); + card_ptr popped_card = pop(initial_deck); + PRINTCARD(popped_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npop\n"); + card_ptr popped_card_2 = pop(initial_deck); + PRINTCARD(popped_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nshift\n"); + card_ptr dequeued_card = shift(initial_deck); + PRINTCARD(dequeued_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nshift\n"); + card_ptr dequeued_card_2 = shift(initial_deck); + PRINTCARD(dequeued_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nunshift 2H\n"); + unshift(initial_deck, dequeued_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nunshift 3H\n"); + unshift(initial_deck, dequeued_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npush 4H\n"); + push(initial_deck, popped_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); +} + +void print_pile_ptrs(pile *pile) { + printf("@["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" @ %p | ", c); + + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + diff --git a/solitaire-cli-master_fixed_mac/LICENSE b/solitaire-cli-master_fixed_mac/LICENSE new file mode 100644 index 0000000..3d7786e --- /dev/null +++ b/solitaire-cli-master_fixed_mac/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Juraj Borza + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/solitaire-cli-master_fixed_mac/Makefile b/solitaire-cli-master_fixed_mac/Makefile new file mode 100644 index 0000000..b91ee8b --- /dev/null +++ b/solitaire-cli-master_fixed_mac/Makefile @@ -0,0 +1,8 @@ +CC=cc +sol: + $(CC) -o solitaire -DUNICODE -Wall main.c -lncurses +debug: + $(CC) -g -o solitaire -DUNICODE -Wall main.c -lncurses +ascii: + $(CC) -o solitaire -Wall main.c -lncurses + diff --git a/solitaire-cli-master_fixed_mac/gdb-entitlement.xml b/solitaire-cli-master_fixed_mac/gdb-entitlement.xml new file mode 100644 index 0000000..3d60e8b --- /dev/null +++ b/solitaire-cli-master_fixed_mac/gdb-entitlement.xml @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.debugger + + + diff --git a/solitaire-cli-master_fixed_mac/highScore.txt b/solitaire-cli-master_fixed_mac/highScore.txt new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/solitaire-cli-master_fixed_mac/highScore.txt @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/solitaire-cli-master_fixed_mac/main.c b/solitaire-cli-master_fixed_mac/main.c new file mode 100644 index 0000000..a7dbee1 --- /dev/null +++ b/solitaire-cli-master_fixed_mac/main.c @@ -0,0 +1,881 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +int HIGH_SCORE; + + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Current Score: %d (Last High Score is %d)", state->score, HIGH_SCORE); + move(rows - 1, 0); + printw("solitaire-cli (h for help)> "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //#c#c# + char *pattern_single_move = "c%d %c%d"; //c#c# + char *pattern_single_move2 = "%d %d"; //## + char *pattern_waste_move = "w %c%d"; //wf# or wc# + char *pattern_multi_stock = "%ds"; //#s + char *pattern_stock = "s"; //s + char *pattern_help = "h"; //h + char *pattern_quit = "quit"; //quit + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else if (strcmp(command, pattern_quit) == 0) { + parsed.source = 'q'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP, + MOVE_QUIT +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#, quit", + "quit"}; + +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + //catch source is quit - just display message for the moment + if(parsed.source == 'q' ) + { + return MOVE_QUIT; + } + + //catch source / destination too high + if((parsed.destination == 'c' && ((parsed.destination_index >= COLUMN_COUNT + 1) || (parsed.destination_index < 1))) + || (parsed.destination == 'f' && ((parsed.destination_index >= FOUNDATION_COUNT + 1) || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + // source_index can also be broken + if(parsed.source == 'c' && ((parsed.source_index >= COLUMN_COUNT + 1) || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + + FILE *fptr; + + fptr = fopen("highScore.txt","r"); + + if(fptr == NULL){ + HIGH_SCORE =0; + } else { + fscanf(fptr, "%i",&HIGH_SCORE); + } + + fclose(fptr); + + + + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + if (result == MOVE_QUIT) { + // add these seems cannot save the command line + // print_all_curses(state); + //end_curses(); + //initscr(); // visual mode, no need if not + //erase(); + refresh(); + getch(); + endwin(); + printf("high score is %d\n", state->score); // may save and launch with the overall high score ??? + + fptr = fopen("highScore.txt","w"); + + if(fptr == NULL){ + printf("Error write HIGH_SCORE"); + exit(1); + } else { + if (state->score < HIGH_SCORE){ + state->score = HIGH_SCORE; + } + fprintf(fptr, "%i",state->score); + } + + fclose(fptr); + + + exit(0); + } else { + print_all_curses(state); + } + } + getch(); // not sure about these 2 lines + end_curses(); // add this but still not ok; the command line broken +} diff --git a/solitaire-cli-master_fixed_mac/main.c.1.txt b/solitaire-cli-master_fixed_mac/main.c.1.txt new file mode 100644 index 0000000..f6a64c6 --- /dev/null +++ b/solitaire-cli-master_fixed_mac/main.c.1.txt @@ -0,0 +1,822 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Score: %d", state->score); + move(rows - 1, 0); + printw("solitaire-cli > "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //#c#c# + char *pattern_single_move = "c%d %c%d"; //c#c# + char *pattern_single_move2 = "%d %d"; //## + char *pattern_waste_move = "w %c%d"; //wf# or wc# + char *pattern_multi_stock = "%ds"; //#s + char *pattern_stock = "s"; //s + char *pattern_help = "h"; //h + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#"}; +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + //catch source / destination too high + if((parsed.destination == 'c' && ((parsed.destination_index >= COLUMN_COUNT + 1) || (parsed.destination_index < 1))) + || (parsed.destination == 'f' && ((parsed.destination_index >= FOUNDATION_COUNT + 1) || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + // source_index can also be broken + if(parsed.source == 'c' && ((parsed.source_index >= COLUMN_COUNT + 1) || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + print_all_curses(state); + } + getch(); + end_curses(); +} diff --git a/solitaire-cli-master_fixed_mac/main.c.2.txt b/solitaire-cli-master_fixed_mac/main.c.2.txt new file mode 100644 index 0000000..092eb25 --- /dev/null +++ b/solitaire-cli-master_fixed_mac/main.c.2.txt @@ -0,0 +1,843 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Score: %d", state->score); + move(rows - 1, 0); + printw("solitaire-cli > "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //#c#c# + char *pattern_single_move = "c%d %c%d"; //c#c# + char *pattern_single_move2 = "%d %d"; //## + char *pattern_waste_move = "w %c%d"; //wf# or wc# + char *pattern_multi_stock = "%ds"; //#s + char *pattern_stock = "s"; //s + char *pattern_help = "h"; //h + char *pattern_quit = "quit"; //quit + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else if (strcmp(command, pattern_quit) == 0) { + parsed.source = 'q'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP, + MOVE_QUIT +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#, quit", + "quit"}; + +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + //catch source is quit - just display message for the moment + if(parsed.source == 'q' ) + { + return MOVE_QUIT; + } + + //catch source / destination too high + if((parsed.destination == 'c' && ((parsed.destination_index >= COLUMN_COUNT + 1) || (parsed.destination_index < 1))) + || (parsed.destination == 'f' && ((parsed.destination_index >= FOUNDATION_COUNT + 1) || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + // source_index can also be broken + if(parsed.source == 'c' && ((parsed.source_index >= COLUMN_COUNT + 1) || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + if (result == MOVE_QUIT) { + // add these seems cannot save the command line + // print_all_curses(state); + //getch(); + //end_curses(); + printf("high score is %d\n", state->score); // may save and launch with the overall high score ??? + return (0); + } else { + print_all_curses(state); + } + } + getch(); // not sure about these 2 lines + end_curses(); // add this but still not ok; the command line broken +} diff --git a/solitaire-cli-master_fixed_mac/main.c.3.txt b/solitaire-cli-master_fixed_mac/main.c.3.txt new file mode 100644 index 0000000..a7dbee1 --- /dev/null +++ b/solitaire-cli-master_fixed_mac/main.c.3.txt @@ -0,0 +1,881 @@ +#include +#include +#include +#include +#include + +#include +#include + +//#define DEBUG_PRINT + +int HIGH_SCORE; + + +// utility functions + +void *mallocz(size_t size) { + void *ptr; + ptr = malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +enum { + SUIT_HEART, + SUIT_SPADE, + SUIT_CLUB, + SUIT_DIAMOND, + SUIT_COUNT // ♥♠♣♦ +}; + +enum { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_10, + RANK_J, + RANK_Q, + RANK_K, + RANK_COUNT +}; + +typedef struct card { + int suit; + int rank; + int revealed; +} card; + +#ifdef UNICODE +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "\u2665"; // 2665 BLACK HEART SUIT + case SUIT_SPADE: + return "\u2660"; // 2660 BLACK SPADE SUIT + case SUIT_CLUB: + return "\u2663"; // 2663 BLACK CLUB SUIT + case SUIT_DIAMOND: + return "\u2666"; // 2666 BLACK DIAMOND SUIT + default: + return "?"; + } +} + +#else +const char *suit_to_charptr(int suit) { + switch (suit) { + case SUIT_HEART: + return "H"; + case SUIT_SPADE: + return "S"; + case SUIT_CLUB: + return "C"; + case SUIT_DIAMOND: + return "D"; + default: + return "?"; + } +} +#endif + +const char *rank_to_charptr(int rank) { + switch (rank) { + case RANK_A: + return "A"; + case RANK_2: + return "2"; + case RANK_3: + return "3"; + case RANK_4: + return "4"; + case RANK_5: + return "5"; + case RANK_6: + return "6"; + case RANK_7: + return "7"; + case RANK_8: + return "8"; + case RANK_9: + return "9"; + case RANK_10: + return "10"; + case RANK_J: + return "J"; + case RANK_Q: + return "Q"; + case RANK_K: + return "K"; + default: + return "?"; + } +} + +void print_card(card *c) { + printf("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +} + +void print_card_ptr(card *c) { printf("%p", c); } + +card make_card(int suit, int rank) { + card c; + c.suit = suit; + c.rank = rank; + return c; +} + +card *make_card_ptr(int suit, int rank) { + card *c = mallocz(sizeof(card)); + c->suit = suit; + c->rank = rank; + return c; +} + +int is_black(card c) { return c.suit == SUIT_CLUB || c.suit == SUIT_SPADE; } + +int is_red(card c) { return c.suit == SUIT_HEART || c.suit == SUIT_DIAMOND; } + +int is_ace(card c) { return c.rank == RANK_A; } + +int is_alternate_color(card first, card second) { + return is_black(first) != is_black(second); +} + +int is_in_sequence(card lower, card higher) { + return higher.rank == lower.rank + 1; +} + +int can_be_placed_bottom(card parent, card child) { + return is_alternate_color(parent, child) && is_in_sequence(child, parent); +} + +int is_same_suit(card first, card second) { return first.suit == second.suit; } + +int can_be_placed_on_foundation(card parent, card child) { + return is_same_suit(parent, child) && is_in_sequence(parent, child); +} + +#define CARD_COUNT 52 + +#define PILE_LIST + +#ifdef PILE_LIST + +typedef struct card_node { + card *value; + struct card_node *next; +} card_node; + +typedef struct pile { + card_node *head; + int num_cards; + char type; +} pile; + +card_node *find_tail(pile *pile) { + card_node *tail = pile->head; + if (tail == 0) + return 0; + while (tail->next != 0) { + tail = tail->next; + } + return tail; +} + +card_node *make_node(card *card) { + card_node *node = mallocz(sizeof(card_node)); + node->value = card; + node->next = 0; + return node; +} + +int is_empty(pile *pile) { return pile->num_cards == 0; } + +// remove a card from a pile, relinking the list +void delete (pile *pile, card *card) { + if (is_empty(pile)) { + return; + } + // no previous node for the first item + card_node *prev = NULL; + card_node *current; + for (current = pile->head; current != NULL; + prev = current, current = current->next) { + if (current->value == card) { + // special case if the first item was found + if (prev == NULL) { + pile->head = current->next; + } else { + // skip the current item in the list + prev->next = current->next; + } + pile->num_cards--; + free(current); + return; + } + } +} + +// append to the end of the list +void push(pile *pile, card *card) { + card_node *tail = find_tail(pile); + if (tail == NULL) { + pile->head = make_node(card); + } else { + tail->next = make_node(card); + } + pile->num_cards++; +} + +// remove a card from the end of the list +card *pop(pile *pile) { + pile->num_cards--; + // find the (n-1)th card + card_node *pre_tail = pile->head; + for (int i = 0; i < pile->num_cards - 1; i++) + pre_tail = pre_tail->next; + card_node *tail = pre_tail->next; + card *card = tail->value; + pre_tail->next = 0; + free(tail); + return card; +} + +// append to the beginning of the list +void unshift(pile *pile, card *card) { + card_node *new_head = make_node(card); + new_head->next = pile->head; + pile->head = new_head; + pile->num_cards++; +} + +// remove a card from the beginning of the list +card *shift(pile *pile) { + pile->num_cards--; + card_node *old_head = pile->head; + pile->head = old_head->next; + + card *card = old_head->value; + free(old_head); + return card; +} + +card *peek_card_at(pile *pile, int index) { + card_node *head = pile->head; + for (int i = 0; i < index; i++) + head = head->next; + return head->value; +} + +card *peek(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return pile->head->value; +} + +card *peek_last(pile *pile) { + if (pile->head == NULL) { + return NULL; + } + return peek_card_at(pile, pile->num_cards - 1); +} + +pile *make_pile() { + pile *pile_ptr = mallocz(sizeof(pile)); + pile_ptr->num_cards = 0; + return pile_ptr; +} + +void fill_deck(pile *pile) { + for (int rank = 0; rank < RANK_COUNT; rank++) { + for (int suit = 0; suit < SUIT_COUNT; suit++) { + push(pile, make_card_ptr(suit, rank)); + } + } +} +#endif + +#define COLUMN_COUNT 7 +#define FOUNDATION_COUNT 4 + +enum { + PILE_DECK, + PILE_WASTE, + PILE_FOUNDATION1, + PILE_FOUNDATION2, + PILE_FOUNDATION3, + PILE_FOUNDATION4, + PILE_COLUMN1, + PILE_COLUMN2, + PILE_COLUMN3, + PILE_COLUMN4, + PILE_COLUMN5, + PILE_COLUMN6, + PILE_COLUMN7, + PILE_COUNT +}; + +char pile_types[] = "dwffffccccccc"; + +typedef struct game_state { + pile **piles; + int pile_count; + int score; +} game_state; + +game_state *make_game_state() { + game_state *state = mallocz(sizeof(game_state)); + state->piles = mallocz(sizeof(pile *) * PILE_COUNT); + for (int pile_idx = 0; pile_idx < PILE_COUNT; pile_idx++) { + state->piles[pile_idx] = make_pile(); + state->piles[pile_idx]->type = pile_types[pile_idx]; + } + return state; +} + +void print_deck(pile *pile) { + printf("["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" "); + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + +void insert(pile *pile, card *card, int idx) { + card_node *pre_tail = pile->head; + for (int i = 0; i < idx; i++) + pre_tail = pre_tail->next; + card_node *card_node = make_node(card); + card_node->next = pre_tail->next; + pre_tail->next = card_node; + pile->num_cards++; +} + +void shuffle_pile(pile *pile) { + int shuffle_times = pile->num_cards * 10; + for (int i = 0; i < shuffle_times; i++) { + // unshift a card and insert to random place + int idx = rand() % pile->num_cards - 1; + card *card = shift(pile); + insert(pile, card, idx); + } +} + +#define PRINTCARD(x) \ + print_card(x); \ + printf(" "); \ + print_card_ptr(x); \ + printf("\n"); + +pile *stock(game_state *state) { return state->piles[PILE_DECK]; } + +pile *waste(game_state *state) { return state->piles[PILE_WASTE]; } + +pile *column(game_state *state, int index_one_based) { + return state->piles[PILE_COLUMN1 + index_one_based - 1]; +} + +pile *foundation(game_state *state, int index_one_based) { + return state->piles[PILE_FOUNDATION1 + index_one_based - 1]; +} + +// returns 1 if a card was revealed +int reveal(card *card) { + if (card == NULL) + return 0; + card->revealed = 1; + return 1; +} + +void hide(card *card) { + if (card == NULL) + return; + card->revealed = 0; +} + +void turn(game_state *state) { + // moves 1 card from stock to waste + card *revealed_card = shift(stock(state)); + reveal(revealed_card); + push(state->piles[PILE_WASTE], revealed_card); +} + +void deal(game_state *state) { + // assuming a shuffled deck + pile *deck = state->piles[PILE_DECK]; + // deal columns + for (int i = 0; i < COLUMN_COUNT; i++) { + int column_idx = i + 1; + pile *column = state->piles[PILE_COLUMN1 + i]; + // deal N cards in Nth column + for (int card_num = 0; card_num < column_idx; card_num++) { + card *card = shift(deck); + push(column, card); + // reveal last card from the column + if (card_num == column_idx - 1) { + reveal(card); + } + } + } + // reveal 1 card + turn(state); +} + +int rows, cols; + +#define BLACK_PAIR 1 +#define RED_PAIR 2 +#define DEFAULT_COLOR -1 + +void init_curses() { + initscr(); + keypad(stdscr, TRUE); + use_default_colors(); + start_color(); + getmaxyx(stdscr, rows, cols); + init_pair(BLACK_PAIR, DEFAULT_COLOR, DEFAULT_COLOR); + init_pair(RED_PAIR, COLOR_RED, DEFAULT_COLOR); +} + +void printw_card(card *c) { + if (c == NULL) { + printw("[ ]"); + return; + } + if (c->revealed) { + int color_pair = is_black(*c) ? BLACK_PAIR : RED_PAIR; + attron(COLOR_PAIR(color_pair)); + printw("%s%s", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); + attroff(COLOR_PAIR(color_pair)); + } else { +#ifdef DEBUG_PRINT + printw("(%s%s)", rank_to_charptr(c->rank), suit_to_charptr(c->suit)); +#else + printw("[ ]"); +#endif + } +} + +void printw_pile_size(pile *pile) { printw("(%d cards)", pile->num_cards); } + +void end_curses() { endwin(); } + +char *first_row_headers[] = {"Stock", "Waste", "", + "Foundation 1", "Foundation 2", "Foundation 3", + "Foundation 4"}; +char *second_row_headers[] = {"Column 1", "Column 2", "Column 3", "Column 4", + "Column 5", "Column 6", "Column 7"}; + +void print_prompt(game_state *state) { + move(rows - 3, 0); + printw("Current Score: %d (Last High Score is %d)", state->score, HIGH_SCORE); + move(rows - 1, 0); + printw("solitaire-cli (h for help)> "); +} + +void debug_print_pile(pile *pile, int row, int column) { + for (int i = 0; i < pile->num_cards; i++) { + move(row + i, column); + printw_card(peek_card_at(pile, i)); + } +} + +void print_all_curses(game_state *state) { + // 2 rows, 7 columns + // top row has a fixed height of 1 card + // bottom row can have up to 13 cards + move(0, 0); + // first row header + // let's assume 100 characters terminal + int column_size = 14; + for (int i = 0; i < 7; i++) { + move(0, column_size * i); + printw("%s", first_row_headers[i]); + } + pile *stock_pile = stock(state); + pile *waste_pile = waste(state); + // first row content + move(1, 0); + printw_card(peek(stock_pile)); + move(2, 0); + printw_pile_size(stock_pile); + move(1, column_size); + printw_card(peek_last(waste_pile)); + move(2, column_size); + printw_pile_size(waste_pile); + + // foundations + for (int f = 0; f < FOUNDATION_COUNT; f++) { + int foundation_1_column = 3; + move(1, (foundation_1_column + f) * column_size); + printw_card(peek_last(foundation(state, f + 1))); + move(2, (foundation_1_column + f) * column_size); + printw_pile_size(foundation(state, f + 1)); + } + + // second row header + for (int i = 0; i < COLUMN_COUNT; i++) { + move(4, column_size * i); + printw("%s", second_row_headers[i]); + move(5, column_size * i); + printw_pile_size(column(state, i + 1)); + } + + for (int i = 0; i < COLUMN_COUNT; i++) { + pile *col = column(state, i + 1); + int base_row = 6; + for (int c = 0; c < col->num_cards; c++) { + move(base_row + c, column_size * i); + printw_card(peek_card_at(col, c)); + } + } + +#ifdef DEBUG_PRINT + // debug: stock, waste + mvprintw(17, 0, "stock:"); + debug_print_pile(stock_pile, 18, 0); + mvprintw(17, 16, "waste:"); + debug_print_pile(waste_pile, 18, 16); + mvprintw(17, 32, "foundation 1:"); + debug_print_pile(foundation(state, 1), 18, 32); + mvprintw(17, 48, "foundation 2:"); + debug_print_pile(foundation(state, 2), 18, 48); + mvprintw(17, 64, "foundation 3:"); + debug_print_pile(foundation(state, 3), 18, 64); + mvprintw(17, 80, "foundation 4:"); + debug_print_pile(foundation(state, 4), 18, 80); +#endif + + // status bar for the commands + print_prompt(state); +} + +void prepare_game(game_state *state) { + pile *stock_pile = stock(state); + fill_deck(stock_pile); + shuffle_pile(stock_pile); + deal(state); + state->score = 0; +} + +typedef struct parsed_input { + char source; + char destination; + int source_index; + int destination_index; + int source_amount; + int success; +} parsed_input; + +parsed_input parse_input(char *command) { + parsed_input parsed; + parsed.success = 1; + parsed.source_amount = 1; + // parser patterns + char *pattern_multi_move = "%dc%d c%d"; //#c#c# + char *pattern_single_move = "c%d %c%d"; //c#c# + char *pattern_single_move2 = "%d %d"; //## + char *pattern_waste_move = "w %c%d"; //wf# or wc# + char *pattern_multi_stock = "%ds"; //#s + char *pattern_stock = "s"; //s + char *pattern_help = "h"; //h + char *pattern_quit = "quit"; //quit + if (sscanf(command, pattern_multi_move, &parsed.source_amount, + &parsed.source_index, &parsed.destination_index) == 3) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_single_move, &parsed.source_index, + &parsed.destination, &parsed.destination_index) == 3) { + parsed.source = 'c'; + } else if (sscanf(command, pattern_waste_move, &parsed.destination, + &parsed.destination_index) == 2) { + parsed.source = 'w'; + } else if (sscanf(command, pattern_single_move2, &parsed.source_index, + &parsed.destination_index) == 2) { + parsed.source = 'c'; + parsed.destination = 'c'; + } else if (sscanf(command, pattern_multi_stock, &parsed.source_amount) == 1) { + parsed.source = 's'; + } else if (strcmp(command, pattern_stock) == 0) { + parsed.source = 's'; + } else if (strcmp(command, pattern_help) == 0) { + parsed.source = 'h'; + } else if (strcmp(command, pattern_quit) == 0) { + parsed.source = 'q'; + } else { + parsed.success = 0; + } + return parsed; +} + +pile *get_pile(game_state *state, char pile_prefix, int pile_index_one_based) { + switch (pile_prefix) { + case 's': + return stock(state); + case 'w': + return waste(state); + case 'f': + return foundation(state, pile_index_one_based); + case 'c': + return column(state, pile_index_one_based); + default: + return NULL; + } +} + +void add_score(game_state *state, int score) { + state->score += score; + if (state->score < 0) { + state->score = 0; + } +} + +enum { + MOVE_OK, + MOVE_INVALID_COMMAND, + MOVE_SOURCE_EMPTY, + MOVE_INVALID_MOVE, + MOVE_TOO_MANY_CARDS, + MOVE_CANNOT_REDEAL, + MOVE_INVALID_DESTINATION, + MOVE_INVALID_SOURCE, + MOVE_HELP, + MOVE_QUIT +}; +char *move_results[] = {"OK", + "Invalid command", + "Source pile empty", + "Invalid move", + "Too many cards to move!", + "Cannot redeal, stock pile empty", + "Invalid destination", + "Invalid source", + "h, s, #s, wc#, wf#, ##, c#c#, c#f#, #c#c#, quit", + "quit"}; + +void move_card(game_state *state, card *card, pile *source_pile, + pile *destination_pile) { + delete (source_pile, card); + if (reveal(peek_last(source_pile))) { + add_score(state, 5); // turn over column card + } + push(destination_pile, card); + + // add score for the moves + if (destination_pile->type == 'f') { + add_score(state, 10); + } + if (source_pile->type == 'w' && destination_pile->type == 'c') { + add_score(state, 5); + } +} + +void redeal(game_state *state) { + while (!is_empty(waste(state))) { + card *card = shift(waste(state)); + hide(card); + push(stock(state), card); + } + add_score(state, -100); +} + +int attempt_move(game_state *state, char *command) { + // format: c6 f3 + parsed_input parsed = parse_input(command); + if (parsed.success != 1) { + return MOVE_INVALID_COMMAND; + } + + //catch source is help + if(parsed.source == 'h' ) + { + return MOVE_HELP; + } + + //catch source is quit - just display message for the moment + if(parsed.source == 'q' ) + { + return MOVE_QUIT; + } + + //catch source / destination too high + if((parsed.destination == 'c' && ((parsed.destination_index >= COLUMN_COUNT + 1) || (parsed.destination_index < 1))) + || (parsed.destination == 'f' && ((parsed.destination_index >= FOUNDATION_COUNT + 1) || (parsed.destination_index < 1)))) + { + return MOVE_INVALID_DESTINATION; + } + + // source_index can also be broken + if(parsed.source == 'c' && ((parsed.source_index >= COLUMN_COUNT + 1) || (parsed.source_index < 1))){ + return MOVE_INVALID_SOURCE; + } + + // figure out destination + if (parsed.source == 's') { + for (int i = 0; i < parsed.source_amount; i++) { + if (is_empty(stock(state))) { + // try to redeal + if (is_empty(waste(state))) { + return MOVE_CANNOT_REDEAL; + } + redeal(state); + } + turn(state); + } + return MOVE_OK; + } + pile *source_pile = get_pile(state, parsed.source, parsed.source_index); + pile *destination_pile = + get_pile(state, parsed.destination, parsed.destination_index); + + // check if the move is valid + if (is_empty(source_pile)) { + return MOVE_SOURCE_EMPTY; + } + + if (source_pile->num_cards < parsed.source_amount) { + return MOVE_TOO_MANY_CARDS; + } + + int first_card_index = source_pile->num_cards - parsed.source_amount; + // multi-card move + if (parsed.source_amount > 1) { + // check if all cards have been revealed + card *c = peek_card_at(source_pile, first_card_index); + if (c->revealed == 0) { + return MOVE_TOO_MANY_CARDS; + } + } + + for (int card_index = 0; card_index < parsed.source_amount; card_index++) { + + // card index doesn't move - the card is always at the same index + card *source_card = peek_card_at(source_pile, first_card_index); + + // check if the move is valid based on the destination type + if (parsed.destination == 'f') { + // only ace goes if the destination is empty + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_A) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + // non-empty foundation, pick up the first card + card *top_foundation_card = peek_last(destination_pile); + if (can_be_placed_on_foundation(*top_foundation_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else if (parsed.destination == 'c') { + // king can go in an empty column + if (is_empty(destination_pile)) { + if (source_card->rank == RANK_K) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } else { + card *bottom_column_card = peek_last(destination_pile); + if (can_be_placed_bottom(*bottom_column_card, *source_card)) { + move_card(state, source_card, source_pile, destination_pile); + } else { + return MOVE_INVALID_MOVE; + } + } + } else { + return MOVE_INVALID_DESTINATION; + } + } + + // set the return code + return MOVE_OK; +} + +int main() { + + FILE *fptr; + + fptr = fopen("highScore.txt","r"); + + if(fptr == NULL){ + HIGH_SCORE =0; + } else { + fscanf(fptr, "%i",&HIGH_SCORE); + } + + fclose(fptr); + + + + srand(time(NULL)); + // srand(3); + setlocale(LC_ALL, ""); + init_curses(); + + // prepare the game state + game_state *state = make_game_state(); + prepare_game(state); + + char buffer[80]; + print_all_curses(state); + + // game loop + while (1) { + getstr(buffer); + erase(); + // pick up the source, destination and attempt the move + int result = attempt_move(state, buffer); + mvprintw(rows - 2, 0, "Move status: %s", move_results[result]); + // show new status in the status bar + if (result == MOVE_QUIT) { + // add these seems cannot save the command line + // print_all_curses(state); + //end_curses(); + //initscr(); // visual mode, no need if not + //erase(); + refresh(); + getch(); + endwin(); + printf("high score is %d\n", state->score); // may save and launch with the overall high score ??? + + fptr = fopen("highScore.txt","w"); + + if(fptr == NULL){ + printf("Error write HIGH_SCORE"); + exit(1); + } else { + if (state->score < HIGH_SCORE){ + state->score = HIGH_SCORE; + } + fprintf(fptr, "%i",state->score); + } + + fclose(fptr); + + + exit(0); + } else { + print_all_curses(state); + } + } + getch(); // not sure about these 2 lines + end_curses(); // add this but still not ok; the command line broken +} diff --git a/solitaire-cli-master_fixed_mac/solitaire b/solitaire-cli-master_fixed_mac/solitaire new file mode 100755 index 0000000..88a5eb4 Binary files /dev/null and b/solitaire-cli-master_fixed_mac/solitaire differ diff --git a/solitaire-cli-master_fixed_mac/solitaire.dSYM/Contents/Info.plist b/solitaire-cli-master_fixed_mac/solitaire.dSYM/Contents/Info.plist new file mode 100644 index 0000000..f38ed14 --- /dev/null +++ b/solitaire-cli-master_fixed_mac/solitaire.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.solitaire + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/solitaire-cli-master_fixed_mac/solitaire.dSYM/Contents/Resources/DWARF/solitaire b/solitaire-cli-master_fixed_mac/solitaire.dSYM/Contents/Resources/DWARF/solitaire new file mode 100644 index 0000000..f4dec3d Binary files /dev/null and b/solitaire-cli-master_fixed_mac/solitaire.dSYM/Contents/Resources/DWARF/solitaire differ diff --git a/solitaire-cli-master_fixed_mac/test.c b/solitaire-cli-master_fixed_mac/test.c new file mode 100644 index 0000000..923f56b --- /dev/null +++ b/solitaire-cli-master_fixed_mac/test.c @@ -0,0 +1,92 @@ + +void test_cards() { + card c5H = make_card(SUIT_HEART, RANK_5); + card c5S = make_card(SUIT_SPADE, RANK_5); + card c6H = make_card(SUIT_HEART, RANK_6); + card c6D = make_card(SUIT_DIAMOND, RANK_6); + card c5D = make_card(SUIT_DIAMOND, RANK_5); + card c7D = make_card(SUIT_DIAMOND, RANK_7); + printf("5s is black %d vs 1 \n", is_black(c5S)); + printf("5s is red %d vs 0 \n", is_red(c5S)); + printf("6h is red %d vs 1 \n", is_red(c6H)); + printf("6h is black %d vs 0 \n", is_black(c6H)); + printf("5h 6h is alternate %d vs 0 \n", is_alternate_color(c5H, c6H)); + printf("5s 6h is alternate %d vs 1 \n", is_alternate_color(c5S, c6H)); + printf("5d 6d is same suit %d vs 1 \n", is_same_suit(c5D, c6D)); + printf("6h 6d is same suit %d vs 0 \n", is_same_suit(c6H, c6D)); + printf("5s 6h in sequence %d vs 1 \n", is_in_sequence(c5S, c6H)); + printf("6h 6d in sequence %d vs 0 \n", is_in_sequence(c6H, c6D)); + printf("5s 7d in sequence %d vs 0 \n", is_in_sequence(c5S, c7D)); + printf("5h 6h can be placed on foundation %d vs 1\n", + can_be_placed_on_foundation(c5H, c6H)); + printf("5h 6h can be placed on foundation %d vs 0\n", + can_be_placed_on_foundation(c6H, c5H)); + printf("6h 5s can be placed on the bottom %d vs 1\n", + can_be_placed_bottom(c6H, c5S)); + printf("5h 6h can be placed on the bottom %d vs 0\n", + can_be_placed_bottom(c5H, c6H)); + printf("6h 5h can be placed on the bottom %d vs 0\n", + can_be_placed_bottom(c6H, c5H)); + // TODO can_be_placed_on_bottom test +} + + +void test_pile_operations() { + pile *initial_deck = make_pile(); + fill_deck(initial_deck); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npop\n"); + card_ptr popped_card = pop(initial_deck); + PRINTCARD(popped_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npop\n"); + card_ptr popped_card_2 = pop(initial_deck); + PRINTCARD(popped_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nshift\n"); + card_ptr dequeued_card = shift(initial_deck); + PRINTCARD(dequeued_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nshift\n"); + card_ptr dequeued_card_2 = shift(initial_deck); + PRINTCARD(dequeued_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nunshift 2H\n"); + unshift(initial_deck, dequeued_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\nunshift 3H\n"); + unshift(initial_deck, dequeued_card); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); + + printf("\npush 4H\n"); + push(initial_deck, popped_card_2); + print_deck(initial_deck); + print_pile_ptrs(initial_deck); +} + +void print_pile_ptrs(pile *pile) { + printf("@["); + card_node *head = pile->head; + for (int i = 0; i < pile->num_cards; i++) { + card *c = head->value; + print_card(c); + printf(" @ %p | ", c); + + head = head->next; + } + printf("] (%d)\n", pile->num_cards); +} + diff --git a/solitaire.dSYM/Contents/Info.plist b/solitaire.dSYM/Contents/Info.plist new file mode 100644 index 0000000..f38ed14 --- /dev/null +++ b/solitaire.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.solitaire + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/solitaire.dSYM/Contents/Resources/DWARF/solitaire b/solitaire.dSYM/Contents/Resources/DWARF/solitaire new file mode 100644 index 0000000..41eeefc Binary files /dev/null and b/solitaire.dSYM/Contents/Resources/DWARF/solitaire differ diff --git a/vimrc b/vimrc new file mode 100644 index 0000000..38cf1b6 --- /dev/null +++ b/vimrc @@ -0,0 +1,26 @@ +syntax on +filetype plugin indent on +" expand tab 2 spaces +set expandtab + +set tabstop=2 +set softtabstop=2 +" indenting with > +set shiftwidth=2 + +autocmd FileType c,cpp setlocal equalprg=clang-format + +" map return key in insert mode to produce ctrl+y in (autocomplete) popup menu +inoremap pumvisible() ? "\" : "\" + +" tab completion - tab on line beginning, else autocomplete +function! InsertTabWrapper() + let col = col('.') - 1 + if !col || getline('.')[col - 1] !~ '\k' + return "\" + else + return "\" + endif +endfunction +inoremap InsertTabWrapper() +inoremap