From b8a8ab89be40f19b297ca3b56f0ef98525c3dacf Mon Sep 17 00:00:00 2001 From: girst Date: Tue, 30 Oct 2018 20:21:46 +0100 Subject: [PATCH] first working version of canfield - undo not yet implemented (req. rework of stock/waste) - spider move cards t2t() not implemented - keyboard function quits game on any invalid (non-numeric) input - many TODOs in readme, code --- Makefile | 18 +++ README.md | 74 +++++++++++ schemes.h | 154 +++++++++++++++++++++++ sol.c | 370 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sol.h | 83 ++++++++++++ 5 files changed, 699 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 schemes.h create mode 100644 sol.c create mode 100644 sol.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2aaab4d --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +.PHONY: all clean + +CFLAGS := -Wall -Wextra -pedantic -std=c99 -g3 -Wno-unused-parameter + +all: sol + +#canfield: +sol: sol.c sol.h schemes.h + $(CC) $(CFLAGS) -DKLONDIKE $< -o $@ + +spider: sol.c sol.h schemes.h + $(CC) $(CFLAGS) -DSPIDER $< -o $@ + +clean: + rm -f sol spider + +getfuns: + grep -o '^\w.* \w.*(.*)[^/]*{' sol.c|sed 's/ *{$/;/' diff --git a/README.md b/README.md new file mode 100644 index 0000000..48ffb37 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# solVIItaire + +play klondike and spider solitaire in your unicode terminal. + +## TODO + + * TODO: DATA STRUCTURES FOR UNDO +CURRENT DS FOR STACK/WASTE ("move over") IS INCOMPATIBLE WITH UNDO!!!! +keeping taken card slots empty allows them to be reinserted by undo() + * TODO: extreme overlapping: if we are printing a sequence or multiple face + down cards, only print `.overlap` lines of the ends, and `1` line for the + middle cards + * TODO: hjkl keyboard mode + * TODO: highlight `from` pile, so users can see at what input stage they are + * TODO: spider keyboard: `` stacks; 1-9,0=tableu, return=draw + * TODO: use `#ifdef`s to differentiate games (sol, spider, ed-sol, ed-spider) + * TODO: patience: allow taking from 0(foundation) + * TODO: s/KLONDIKE/PATIENCE/g + * TODO: scores (see !w), variants: draw 3, max. n overturns + * TODO: undo + * TODO: vt220 mode + * TODO: ed(1) mode (solEDaire): playable on a line printer; ascii/ibm only? + +## Notes + + - terminology: +``` + {stock}[waste] [4*foundation] + + [] {} {} {} {} {} {} + [] (tableu piles) {} {} + [] {} {} {} {} + [] {} {} {} + [] {} {} + [] {} + [] +``` + - data structures: + - enum for each card (e.g. `SPADES_ACE`, `HEARTS_10`) + - each pile is an array holding (13 open cards + up to 6 closed) + [0] is the "northmost"/bottom-most card; unoccupied are NULL/NO_CARD + - a single card is represented in the 'cards' enum; if it is closed, it is negated. + - the foundation are 4 arrays of size 13 + - the stock pile is an array holding n cards and an index to the one to display + when removing, decrement stack size and move all cards above index 1 over + - previous states array: where to move which cards to get back to the state before + - undo: + double-linked list (follow `.prev` to undo, `.next` to redo) + "N cards were moved from X to Y" (do Y->X to undo) + allows jumping forwards in time as well (by repeating X->Y) + warn: when appending state, must check if `.next` was non-NULL and free rest of chain if so. + - multiple card sizes: schemes.h will store cards like below. if we want to draw a card + that has one or more other cards below it, we only draw the first `.overlap` lines, + otherwise if it is the last one, we draw the whole one. + this will give a look like in `~/solitaire-tests` +``` + .grid=7, //vertical alignment + .overlap=2, + .cards = [ + ["╭───╮", + "│♠ X│", + "│ ♠ │", + "╰───╯", + NULL], + ] + //or: + .grid=2, + .overlap=1, + .cards = [ + [ "🃖 ", NULL ], + ] +``` +"open": face up card +"closed": face down card diff --git a/schemes.h b/schemes.h new file mode 100644 index 0000000..00c42e3 --- /dev/null +++ b/schemes.h @@ -0,0 +1,154 @@ +#ifndef __SCHEMES_H__ +#define __SCHEMES_H__ + +#include "sol.h" + +struct scheme { + int width; /* column alignment */ + int height; /* height of a card */ //TODO: obsoletes NULL termination + int overlap; /* no of lines to draw when cards overlapp */ + char** card[_NUM_CARDS_internal]; + char** facedown; + char** placeholder; +}; + +#define ULCARD(s, r) (char*[]) \ +{"╭───╮",\ + "│"s" "r"│",\ + "│ "s" │",\ + "╰───╯", NULL} +#define RULCARD(s, r) \ + ULCARD("\033[91m" s "\033[0m", r) +#define BULCARD(s, r) \ + ULCARD("\033[37m" s "\033[0m", r) +#define USCARD(c) (char*[]){c, NULL} + +const struct scheme unicode_large_mono = { + .width = 5, + .height = 4, + .overlap = 2, + .card = { + [NO_CARD] = (char*[]){" "," "," "," ", NULL}, + [CLU_A] = ULCARD("♣","A"), [DIA_A] = ULCARD("♦","A"), + [HEA_A] = ULCARD("♥","A"), [SPA_A] = ULCARD("♠","A"), + [CLU_2] = ULCARD("♣","2"), [DIA_2] = ULCARD("♦","2"), + [HEA_2] = ULCARD("♥","2"), [SPA_2] = ULCARD("♠","2"), + [CLU_3] = ULCARD("♣","3"), [DIA_3] = ULCARD("♦","3"), + [HEA_3] = ULCARD("♥","3"), [SPA_3] = ULCARD("♠","3"), + [CLU_4] = ULCARD("♣","4"), [DIA_4] = ULCARD("♦","4"), + [HEA_4] = ULCARD("♥","4"), [SPA_4] = ULCARD("♠","4"), + [CLU_5] = ULCARD("♣","5"), [DIA_5] = ULCARD("♦","5"), + [HEA_5] = ULCARD("♥","5"), [SPA_5] = ULCARD("♠","5"), + [CLU_6] = ULCARD("♣","6"), [DIA_6] = ULCARD("♦","6"), + [HEA_6] = ULCARD("♥","6"), [SPA_6] = ULCARD("♠","6"), + [CLU_7] = ULCARD("♣","7"), [DIA_7] = ULCARD("♦","7"), + [HEA_7] = ULCARD("♥","7"), [SPA_7] = ULCARD("♠","7"), + [CLU_8] = ULCARD("♣","8"), [DIA_8] = ULCARD("♦","8"), + [HEA_8] = ULCARD("♥","8"), [SPA_8] = ULCARD("♠","8"), + [CLU_9] = ULCARD("♣","9"), [DIA_9] = ULCARD("♦","9"), + [HEA_9] = ULCARD("♥","9"), [SPA_9] = ULCARD("♠","9"), + [CLU_X] = ULCARD("♣","X"), [DIA_X] = ULCARD("♦","X"), + [HEA_X] = ULCARD("♥","X"), [SPA_X] = ULCARD("♠","X"), + [CLU_J] = ULCARD("♣","J"), [DIA_J] = ULCARD("♦","J"), + [HEA_J] = ULCARD("♥","J"), [SPA_J] = ULCARD("♠","J"), + [CLU_Q] = ULCARD("♣","Q"), [DIA_Q] = ULCARD("♦","Q"), + [HEA_Q] = ULCARD("♥","Q"), [SPA_Q] = ULCARD("♠","Q"), + [CLU_K] = ULCARD("♣","K"), [DIA_K] = ULCARD("♦","K"), + [HEA_K] = ULCARD("♥","K"), [SPA_K] = ULCARD("♠","K"), + }, + .facedown = (char*[]){ + "╭───╮", + "│▚▚▚│", + "│▚▚▚│", + "╰───╯", NULL + }, + .placeholder = (char*[]){ + "╭╌╌╌╮", //┄┈ + "╎ ╎", //┆┊ + "╎ ╎", //┆┊ + "╰╌╌╌╯", NULL + }, +}; +const struct scheme unicode_large_color = { + .width = 5, + .height = 4, + .overlap = 2, + .card = { + [NO_CARD] = (char*[]){" "," "," "," ", NULL}, + [CLU_A] = BULCARD("♣","A"), [DIA_A] = RULCARD("♦","A"), + [HEA_A] = RULCARD("♥","A"), [SPA_A] = BULCARD("♠","A"), + [CLU_2] = BULCARD("♣","2"), [DIA_2] = RULCARD("♦","2"), + [HEA_2] = RULCARD("♥","2"), [SPA_2] = BULCARD("♠","2"), + [CLU_3] = BULCARD("♣","3"), [DIA_3] = RULCARD("♦","3"), + [HEA_3] = RULCARD("♥","3"), [SPA_3] = BULCARD("♠","3"), + [CLU_4] = BULCARD("♣","4"), [DIA_4] = RULCARD("♦","4"), + [HEA_4] = RULCARD("♥","4"), [SPA_4] = BULCARD("♠","4"), + [CLU_5] = BULCARD("♣","5"), [DIA_5] = RULCARD("♦","5"), + [HEA_5] = RULCARD("♥","5"), [SPA_5] = BULCARD("♠","5"), + [CLU_6] = BULCARD("♣","6"), [DIA_6] = RULCARD("♦","6"), + [HEA_6] = RULCARD("♥","6"), [SPA_6] = BULCARD("♠","6"), + [CLU_7] = BULCARD("♣","7"), [DIA_7] = RULCARD("♦","7"), + [HEA_7] = RULCARD("♥","7"), [SPA_7] = BULCARD("♠","7"), + [CLU_8] = BULCARD("♣","8"), [DIA_8] = RULCARD("♦","8"), + [HEA_8] = RULCARD("♥","8"), [SPA_8] = BULCARD("♠","8"), + [CLU_9] = BULCARD("♣","9"), [DIA_9] = RULCARD("♦","9"), + [HEA_9] = RULCARD("♥","9"), [SPA_9] = BULCARD("♠","9"), + [CLU_X] = BULCARD("♣","X"), [DIA_X] = RULCARD("♦","X"), + [HEA_X] = RULCARD("♥","X"), [SPA_X] = BULCARD("♠","X"), + [CLU_J] = BULCARD("♣","J"), [DIA_J] = RULCARD("♦","J"), + [HEA_J] = RULCARD("♥","J"), [SPA_J] = BULCARD("♠","J"), + [CLU_Q] = BULCARD("♣","Q"), [DIA_Q] = RULCARD("♦","Q"), + [HEA_Q] = RULCARD("♥","Q"), [SPA_Q] = BULCARD("♠","Q"), + [CLU_K] = BULCARD("♣","K"), [DIA_K] = RULCARD("♦","K"), + [HEA_K] = RULCARD("♥","K"), [SPA_K] = BULCARD("♠","K"), + }, + .facedown = (char*[]){ + "╭───╮", + "│\033[94m▚▚▚\033[0m│", + "│\033[94m▚▚▚\033[0m│", + "╰───╯", NULL + }, + .placeholder = (char*[]){ + "╭╌╌╌╮", //┄┈ + "╎ ╎", //┆┊ + "╎ ╎", //┆┊ + "╰╌╌╌╯", NULL + }, +}; +const struct scheme unicode_small_mono = { + .width = 2, + .height = 1, + .overlap = 1, + .card = { + [NO_CARD] = (char*[]){" ", NULL}, + [CLU_A] = USCARD("🃑 "), [DIA_A] = USCARD("🃁 "), + [HEA_A] = USCARD("🂱 "), [SPA_A] = USCARD("🂡 "), + [CLU_2] = USCARD("🃒 "), [DIA_2] = USCARD("🃂 "), + [HEA_2] = USCARD("🂲 "), [SPA_2] = USCARD("🂢 "), + [CLU_3] = USCARD("🃓 "), [DIA_3] = USCARD("🃃 "), + [HEA_3] = USCARD("🂳 "), [SPA_3] = USCARD("🂣 "), + [CLU_4] = USCARD("🃔 "), [DIA_4] = USCARD("🃄 "), + [HEA_4] = USCARD("🂴 "), [SPA_4] = USCARD("🂤 "), + [CLU_5] = USCARD("🃕 "), [DIA_5] = USCARD("🃅 "), + [HEA_5] = USCARD("🂵 "), [SPA_5] = USCARD("🂥 "), + [CLU_6] = USCARD("🃖 "), [DIA_6] = USCARD("🃆 "), + [HEA_6] = USCARD("🂶 "), [SPA_6] = USCARD("🂦 "), + [CLU_7] = USCARD("🃗 "), [DIA_7] = USCARD("🃇 "), + [HEA_7] = USCARD("🂷 "), [SPA_7] = USCARD("🂧 "), + [CLU_8] = USCARD("🃘 "), [DIA_8] = USCARD("🃈 "), + [HEA_8] = USCARD("🂸 "), [SPA_8] = USCARD("🂨 "), + [CLU_9] = USCARD("🃙 "), [DIA_9] = USCARD("🃉 "), + [HEA_9] = USCARD("🂹 "), [SPA_9] = USCARD("🂩 "), + [CLU_X] = USCARD("🃚 "), [DIA_X] = USCARD("🃊 "), + [HEA_X] = USCARD("🂺 "), [SPA_X] = USCARD("🂪 "), + [CLU_J] = USCARD("🃛 "), [DIA_J] = USCARD("🃋 "), + [HEA_J] = USCARD("🂻 "), [SPA_J] = USCARD("🂫 "), + [CLU_Q] = USCARD("🃝 "), [DIA_Q] = USCARD("🃍 "), + [HEA_Q] = USCARD("🂽 "), [SPA_Q] = USCARD("🂭 "), + [CLU_K] = USCARD("🃞 "), [DIA_K] = USCARD("🃎 "), + [HEA_K] = USCARD("🂾 "), [SPA_K] = USCARD("🂮 "), + }, + .facedown = (char*[]){"🂠 ", NULL}, + .placeholder = (char*[]){"❲❳"/*"▒ "*/, NULL}, +}; +#endif diff --git a/sol.c b/sol.c new file mode 100644 index 0000000..243a3b1 --- /dev/null +++ b/sol.c @@ -0,0 +1,370 @@ +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include + +#include "sol.h" +#include "schemes.h" + +#ifdef KLONDIKE +#define NUM_PILES 7 +#define MAX_HIDDEN 6 /*how many cards are turned over at most in a tableu pile*/ +#define MAX_STOCK 24 /*how many cards can be in the stock at most (=@start)*/ +#define NUM_DECKS 1 +#elif defined SPIDER +#define MAX_HIDDEN 5 +#define NUM_PILES 10 +#define MAX_STOCK 50 /*how many cards can be dealt onto the piles*/ +#define NUM_DECKS 2 +#endif + +#define get_suit(card) \ + ((card-1) % NUM_SUITS) +#define get_rank(card) \ + ((card-1) / NUM_SUITS) +#define get_color(card) \ + ((get_suit(card) ^ get_suit(card)>>1) & 1) + +struct playfield { + //TODO: stock and waste are incompatible with undo{} + card_t s[MAX_STOCK]; /* stock */ + int z; /* stock size */ + int w; /* waste; index into stock (const -1 in spider) */ +#ifdef KLONDIKE + card_t f[NUM_SUITS][MAX_HIDDEN+NUM_RANKS]; /* foundation (oversized to ease find_top()) */ + card_t t[NUM_PILES][MAX_HIDDEN+NUM_RANKS]; /* tableu piles */ +#elif defined SPIDER + card_t f[NUM_DECKS*NUM_COLORS][MAX_HIDDEN+NUM_RANKS]; //each completed set gets put on its own pile, so undo is possible + card_t t[NUM_PILES][MAX_HIDDEN+NUM_RANKS]; +#endif + struct undo { + int from; /* pile cards were taken from */ + int to; /* pile cards were moved to */ + int n; /* number of cards moved */ + struct undo* prev; + struct undo* next; + } u; +} f; +struct opts { + const struct scheme* s; +} op; + +#ifdef KLONDIKE +//declare action as array 10 of array 10 of pointer to function (int,int) returning int +// this stores a function pointer for every takeable action that is then called by execute() +//lines = from, cols = to +int (*action[10][10])(int,int) = { + /* 0 1 2 3 4 5 6 7 8 9 */ +/* 0 */ { nop, f2t, f2t, f2t, f2t, f2t, f2t, f2t, nop, nop }, +/* 1 */ { t2f, nop, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop }, +/* 2 */ { t2f, t2t, nop, t2t, t2t, t2t, t2t, t2t, nop, nop }, +/* 3 */ { t2f, t2t, t2t, nop, t2t, t2t, t2t, t2t, nop, nop }, +/* 4 */ { t2f, t2t, t2t, t2t, nop, t2t, t2t, t2t, nop, nop }, +/* 5 */ { t2f, t2t, t2t, t2t, t2t, nop, t2t, t2t, nop, nop }, +/* 6 */ { t2f, t2t, t2t, t2t, t2t, t2t, nop, t2t, nop, nop }, +/* 7 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop, nop }, +/* 8 */ { nop, nop, nop, nop, nop, nop, nop, nop, nop, s2w }, +/* 9 */ { w2f, w2t, w2t, w2t, w2t, w2t, w2t, w2t, w2s, nop }, +}; +#elif defined SPIDER +int (*action[11][10])(int,int) = { + //piles are zero-indexed in spider; stk=stock + /* 0 1 2 3 4 5 6 7 8 9 */ +/* 0 */ { nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t }, +/* 1 */ { t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t }, +/* 2 */ { t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t, t2t }, +/* 3 */ { t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t, t2t }, +/* 4 */ { t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t, t2t }, +/* 5 */ { t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t, t2t }, +/* 6 */ { t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t, t2t }, +/* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t, t2t }, +/* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop, t2t }, +/* 9 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, nop }, +/*stk*/ { s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t }, +}; +#endif + +int main(int argc, char** argv) { + //op.s = &unicode_small_mono; + //op.s = &unicode_large_mono; + op.s = &unicode_large_color; + raw_mode(1); //TODO: alt.screen, etc. + sol(); + raw_mode(0); +} + +void sol(void) { + f = (const struct playfield){0}; + deal(); + + int from, to; + print_table(); + while (!get_cmd(&from, &to)) { + if (action[from][to](from,to)) { + printf ("\033[?5h"); + fflush (stdout); + usleep (100000); + printf ("\033[?5l"); + } + print_table(); + } +} + +int find_top(card_t* pile) { + int i; + for(i=MAX_HIDDEN+NUM_RANKS-1; i>=0 && !pile[i]; i--); + return i; +} +void turn_over(card_t* pile) { + int top = find_top(pile); + if (pile[top] < 0) pile[top] *= -1; +} +#ifdef KLONDIKE +card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */ + card_t card = f.s[f.w]; + /* move stack one over, so there are no gaps in it: */ + for (int i = f.w; i < f.z-1; i++) + f.s[i] = f.s[i+1]; + f.z--; + f.w--; /* make previous card visible again */ + return card; +} +int t2f(int from, int to) { /* tableu to foundation */ + from--; //remove off-by-one + int top_from = find_top(f.t[from]); + to = get_suit(f.t[from][top_from]); + int top_to = find_top(f.f[to]); + if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A) + || (get_rank(f.f[to][top_to]) == get_rank(f.t[from][top_from])-1)) { + f.f[to][top_to+1] = f.t[from][top_from]; + f.t[from][top_from] = NO_CARD; + turn_over(f.t[from]); + return 0; + } else return 1; +} +int w2f(int from, int to) { /* waste to foundation */ + if (f.w < 0) return 1; + to = get_suit(f.s[f.w]); + int top_to = find_top(f.f[to]); + if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A) + || (get_rank(f.f[to][top_to]) == get_rank(f.s[f.w])-1)) { + f.f[to][top_to+1] = stack_take(); + return 0; + } else return 1; + +} +int s2w(int from, int to) { /* stock to waste */ + if (f.z == 0) return 1; + f.w++; + if (f.w == f.z) f.w = -1; + return 0; +} +int w2s(int from, int to) { /* waste to stock (undoes stock to waste) */ + if (f.z == 0) return 1; + f.w--; + if (f.w < -1) f.w = f.z-1; + return 0; +} +int f2t(int from, int to) { /* foundation to tableu */ + //TODO: there are two possible cards one can take. choosing one isn't implemented yet! + to--; //remove off-by-one + int top_to = find_top(f.t[to]); + from = get_color(f.t[to][top_to]); + int top_from = find_top(f.f[from]); + if ((get_color(f.t[to][top_to]) != get_color(f.f[from][top_from])) + && (get_rank(f.t[to][top_to]) == get_rank(f.f[from][top_from])+1)) { + f.t[to][top_to+1] = stack_take(); + return 0; + } else return 1; +} +int w2t(int from, int to) { //waste to tableu + to--; //remove off-by-one + int top_to = find_top(f.t[to]); + if (((get_color(f.t[to][top_to]) != get_color(f.s[f.w])) + && (get_rank(f.t[to][top_to]) == get_rank(f.s[f.w])+1)) + || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) { + f.t[to][top_to+1] = stack_take(); + return 0; + } else return 1; +} +int t2t(int from, int to) { + from--; to--; //remove off-by-one + int top_to = find_top(f.t[to]); + int top_from = find_top(f.t[from]); + for (int i = top_from; i >=0; i--) { + //TODO: check that we aren't moving facedown cards! + if (((get_color(f.t[to][top_to]) != get_color(f.t[from][i])) + && (get_rank(f.t[to][top_to]) == get_rank(f.t[from][i])+1)) + || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) { + /* move cards [i..top_from] to their destination */ + for (;i <= top_from; i++) { + top_to++; + f.t[to][top_to] = f.t[from][i]; + f.t[from][i] = NO_CARD; + } + turn_over(f.t[from]); + return 0; + } + } + return 1; /* no such move possible */ +} +#elif defined SPIDER +int t2t(int from, int to) { //TODO: implementation XXX + from--; to--; //remove off-by-one + f.t[from][find_top(f.t[from])] = NO_CARD; + return 1; +} +int s2t(int from, int to) { + if (f.z <= 0) return 1; /* stack still has cards? */ + for (int pile = 0; pile < NUM_PILES; pile++) + if (f.t[pile][0] == NO_CARD) return 1; /*no piles may be empty*/ + for (int pile = 0; pile < NUM_PILES; pile++) { + f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z]; + } + return 0; +} +#endif +int nop(int from, int to) { return 1; } + +int get_cmd (int* from, int* to) { + //returns 0 on success or an error code indicating game quit, new game,... + //TODO: only works for KLONDIKE + //TODO: check validity + //TODO: segfault on invalid input! + char f, t; + f = getchar(); +#ifdef SPIDER + if (f=='\n') { + *from = 10; + *to = 0; + return CMD_MOVE; + } +#endif + switch (f) { + case 'q': return CMD_QUIT; + case 'r': return CMD_NEW; + default: if (f < '0' || f > '9') return CMD_INVAL; + } + t = +#ifdef KLONDIKE + (f=='8')?'9': +#endif + getchar(); + *from = f-'0'; + *to = t-'0'; + return CMD_MOVE; +} + +void deal(void) { + card_t deck[DECK_SIZE*NUM_DECKS]; + int avail = DECK_SIZE*NUM_DECKS; + for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1; + srandom (time(NULL)); + for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { //fisher-yates + int j = random() % (i+1); + if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j]; + } + // deal cards + //foundation starts empty: + for (int i = 0; i < NUM_SUITS; i++) + for (int j = NUM_RANKS; j; j--) f.f[i][j] = NO_CARD; + + for (int i = 0; i < NUM_PILES; i++) { +#ifdef KLONDIKE + //tableu: [0] = 0,1; [1] = 1,1; [2] = 2,1; ... [6] = 6,1 + int closed = i; +#elif defined SPIDER + //left 4 piles have 5 hidden + 1 shown + //right6 piles have 4 hidden + 1 shown + int closed = i<4?5:4; +#endif + + for (int j = 0; j < closed; j++) f.t[i][j] = -deck[--avail]; //face-down cards are negative + f.t[i][closed] = deck[--avail]; //the face-up card + for (int j = closed+1; j < MAX_HIDDEN+NUM_RANKS; j++) f.t[i][j] = NO_CARD; + } + //rest of the cards to the stock (should be 50 for spider): + for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail]; + f.w = -1; /* @start: nothing on waste (no waste in spider -> const) */ +} + +void print_table(void) { //{{{ +printf("\033[2J\033[H");//TEMP XXX -- use raw term mode +#ifdef KLONDIKE + //print stock, waste and foundation: + for (int line = 0; line < op.s->height; line++) { + printf ("%s", ( /* stock */ + (f.w < f.z-1)?op.s->facedown + :op.s->placeholder)[line]); + printf ("%s", ( /* waste */ + ((short)f.w >= 0)?op.s->card[f.s[f.w]] //TODO: sometimes segfaults because f.w == (int)((short)-1) + :op.s->placeholder)[line]); + printf ("%s", op.s->card[NO_CARD][line]); /* spacer */ + /* foundation: */ + for (int pile = 0; pile < NUM_SUITS; pile++) { + for (int i = NUM_RANKS-1; i >=0; i--) { + if (f.f[pile][i]) { + printf ("%s", op.s->card[f.f[pile][i]][line]); + goto next; + } + } + printf ("%s", op.s->placeholder[line]); +next:; + } + printf("\n"); + } + printf("\n"); +#endif + //print tableu piles: + int row[NUM_PILES] = {0}; + int line[NUM_PILES]= {0}; + int line_had_card; // :| +int label[NUM_PILES] = {0};//XXX + do { + line_had_card = 0; + for (int pile = 0; pile < NUM_PILES; pile++) { + card_t card = f.t[pile][row[pile]]; + card_t next = f.t[pile][row[pile]+1]; + printf ("%s", ( + (card<0)?op.s->facedown + :op.s->card[card] + )[line[pile]]); + + if (++line[pile] >= (next?op.s->overlap:op.s->height)) { + //TODO: allow extreme overlap on sequences + line[pile]=0; + row[pile]++; + } + if(!card && !label[pile]) label[pile] = 1, printf ("\b\b%d ", (pile+1) % 10);//XXX: print tableu labels + line_had_card |= !!card; + } + printf ("\n"); + } while (line_had_card); +}//}}} + +void append_undo (int n, int f, int t) { + //check if we have to free redo buffer (.next) + //malloc + //update pointers + *NULL; +} + +void raw_mode(int enable) { //{{{ + static struct termios saved_term_mode; + struct termios raw_term_mode; + + if (enable) { + tcgetattr(STDIN_FILENO, &saved_term_mode); + raw_term_mode = saved_term_mode; + raw_term_mode.c_lflag &= ~(ICANON | ECHO); + raw_term_mode.c_cc[VMIN] = 1 ; + raw_term_mode.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode); + } else { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode); + } +} //}}} + +//vim: foldmethod=marker diff --git a/sol.h b/sol.h new file mode 100644 index 0000000..4d542e5 --- /dev/null +++ b/sol.h @@ -0,0 +1,83 @@ +#ifndef __SOL_H__ +#define __SOL_H__ + +#define DECK_SIZE 52 +enum cards { + NO_CARD, + CLU_A, DIA_A, HEA_A, SPA_A, + CLU_2, DIA_2, HEA_2, SPA_2, + CLU_3, DIA_3, HEA_3, SPA_3, + CLU_4, DIA_4, HEA_4, SPA_4, + CLU_5, DIA_5, HEA_5, SPA_5, + CLU_6, DIA_6, HEA_6, SPA_6, + CLU_7, DIA_7, HEA_7, SPA_7, + CLU_8, DIA_8, HEA_8, SPA_8, + CLU_9, DIA_9, HEA_9, SPA_9, + CLU_X, DIA_X, HEA_X, SPA_X, + CLU_J, DIA_J, HEA_J, SPA_J, + CLU_Q, DIA_Q, HEA_Q, SPA_Q, + CLU_K, DIA_K, HEA_K, SPA_K, + _NUM_CARDS_internal +}; +enum colors { + BLK, + RED, + NUM_COLORS +}; +enum suits { + CLUBS, + DIAMONDS, + HEARTS, + SPADES, + NUM_SUITS +}; +enum ranks { + RANK_A, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_9, + RANK_X, + RANK_J, + RANK_Q, + RANK_K, + NUM_RANKS +}; + +enum special_cmds { + CMD_MOVE, + CMD_INVAL, + CMD_QUIT, + CMD_NEW, +}; + +typedef signed char card_t; + +void sol(void); +int find_top(card_t* pile); +void turn_over(card_t* pile); +#ifdef KLONDIKE +card_t stack_take(void); +int t2f(int from, int to); +int w2f(int from, int to); +int s2w(int from, int to); +int w2s(int from, int to); +int f2t(int from, int to); +int w2t(int from, int to); +int t2t(int from, int to); +#elif defined SPIDER +int t2t(int from, int to); +int s2t(int from, int to); +#endif +int nop(int from, int to); +int get_cmd (int* from, int* to); +void deal(void); +void print_table(void); +void append_undo (int n, int f, int t); +void raw_mode(int enable); + +#endif -- 2.39.3