diff options
author | Ian C <ianc@noddybox.co.uk> | 2018-12-21 11:27:26 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2018-12-21 11:27:26 +0000 |
commit | 1169992d50ce930143efe2851d02e7dfa0304e29 (patch) | |
tree | b7c302c559b65a05f0b552244e57b2a4aed6cff2 | |
parent | 859dc1c2044140f7bae42705940e44b4c94bd07e (diff) |
Split code up into separate objects.
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | csol.c | 833 | ||||
-rw-r--r-- | deck.c | 68 | ||||
-rw-r--r-- | deck.h | 53 | ||||
-rw-r--r-- | klondike.c | 513 | ||||
-rw-r--r-- | klondike.h | 36 | ||||
-rw-r--r-- | pile.c | 146 | ||||
-rw-r--r-- | pile.h | 50 | ||||
-rw-r--r-- | util.c | 209 | ||||
-rw-r--r-- | util.h | 43 |
10 files changed, 1133 insertions, 832 deletions
@@ -24,8 +24,16 @@ # LIBS=-lcurses -csol: csol.c - $(CC) -g -o csol csol.c $(LIBS) +OBJS=csol.o deck.o klondike.o pile.o util.o + +csol: $(OBJS) + $(CC) -g -o csol $(OBJS) $(LIBS) + +csol.o: csol.c deck.h pile.h util.h klondike.h +deck.o: deck.c deck.h +klondike.o: klondike.c deck.h pile.h util.h +pile.o: pile.c pile.h deck.h util.h +util.o: util.c util.h deck.h clean: - -rm -f csol csol.exe core + -rm -f csol csol.exe core $(OBJS) @@ -23,840 +23,15 @@ */ #include <stdlib.h> -#include <limits.h> #include <stdio.h> -#include <string.h> -#include <stdarg.h> #include <time.h> #include <curses.h> -typedef enum -{ - NoSuit = 0, - Heart = 'H', - Diamond = 'D', - Club = 'C', - Spade = 'S' -} Suit; - -typedef struct -{ - int value; - Suit suit; -} Card; - -typedef Card Deck[52]; - -typedef struct -{ - int no; - Card *card; -} Pile; - -static void Fatal(const char *p) -{ - endwin(); - fprintf(stderr, "%s\n", p); - exit(EXIT_FAILURE); -} - -static void Init(Deck deck) -{ - int i = 0; - int f; - - for(f=1; f < 14; f++) - { - deck[i].suit = Heart; - deck[i++].value = f; - - deck[i].suit = Diamond; - deck[i++].value = f; - - deck[i].suit = Club; - deck[i++].value = f; - - deck[i].suit = Spade; - deck[i++].value = f; - } -} - -static void Shuffle(Deck deck) -{ - int f; - - for(f = 0; f < 1000; f++) - { - int c1 = rand() % 52; - int c2 = rand() % 52; - Card c; - - c = deck[c1]; - deck[c1] = deck[c2]; - deck[c2] = c; - } -} - -static void AddToPile(Pile *pile, Card card) -{ - pile->no++; - - pile->card = realloc(pile->card, sizeof *pile->card * pile->no); - - if (!pile->card) - { - Fatal("Failed to reallocate memory"); - } - else - { - pile->card[pile->no - 1] = card; - } -} - -static void InsertBottomOfPile(Pile *pile, Card card) -{ - pile->no++; - - pile->card = realloc(pile->card, sizeof *pile->card * pile->no); - - if (!pile->card) - { - Fatal("Failed to reallocate memory"); - } - else - { - int f; - - for(f = pile->no - 1; f > 0; f--) - { - pile->card[f] = pile->card[f - 1]; - } - - pile->card[0] = card; - } -} - -static Card PopPile(Pile *pile) -{ - Card c = {0}; - - if (pile->no) - { - c = pile->card[pile->no - 1]; - - pile->no--; - - if (pile->no) - { - pile->card = realloc(pile->card, sizeof *pile->card * pile->no); - - if (!pile->card) - { - Fatal("Failed to reallocate memory"); - } - } - else - { - free(pile->card); - pile->card = NULL; - } - } - else - { - free(pile->card); - pile->card = NULL; - } - - return c; -} - -static Card TopOfPile(Pile *pile) -{ - Card c = {0}; - - if (pile->no) - { - c = pile->card[pile->no - 1]; - } - - return c; -} - -static void MoveTopCard(Pile *from, Pile *to) -{ - Card c; - - if (from->no) - { - c = PopPile(from); - AddToPile(to, c); - } -} - -static void SwapPile(Pile *a, Pile *b) -{ - Pile t; - - t = *a; - *a = *b; - *b = t; -} - -static void FreePile(Pile *p) -{ - if (p->card) - { - free(p->card); - p->card = NULL; - } -} - -static void Centre(int y, const char *p) -{ - size_t l = strlen(p); - - mvaddstr(y, COLS / 2 - l / 2, p); -} - -static const char *CardName(Card c) -{ - static char buff[32]; - static const char *num[]= - { - "ERR", - " A", - " 2", - " 3", - " 4", - " 5", - " 6", - " 7", - " 8", - " 9", - "10", - " J", - " Q", - " K" - }; - - sprintf(buff, "%s%c", num[c.value], c.suit); - - return buff; -} - -static void DrawCard(int y, int x, int face_down, Card c) -{ - if (c.suit == NoSuit) - { - mvaddstr(y, x, " "); - } - else - { - if (face_down) - { - mvaddstr(y, x, "###"); - } - else - { - if (c.suit == Spade || c.suit == Club) - { - attron(A_REVERSE); - } - - mvaddstr(y, x, CardName(c)); - - if (c.suit == Spade || c.suit == Club) - { - attroff(A_REVERSE); - } - } - } -} - -static double DRand() -{ - return (double)(rand() % 1000) / 1000.0; -} - -static void WinScreen() -{ - static const char *text[] = - { - "# # ## # # # # ## # #", - " # # # # # # # # # # ## #", - " # # # # # # # # # # # ##", - " # # # # # ## ## # # # #", - " # ## ## # # ## # #", - NULL, - }; - - static const int NO_STAR = 100; - - typedef struct - { - double x,y,yi; - } Star; - - Star *star; - - int f; - - star = malloc(sizeof *star * NO_STAR); - - if (!star) - { - Fatal("Memory allocation failed"); - } - - for(f = 0; f < NO_STAR; f++) - { - star[f].x = rand() % COLS; - star[f].y = rand() % LINES; - - do - { - star[f].yi = DRand() * 2; - } while(star[f].yi == 0); - } - - halfdelay(1); - - erase(); - - while(getch() != ' ') - { - for(f = 0; f < NO_STAR; f++) - { - mvaddch(star[f].y, star[f].x, ' '); - - star[f].y -= star[f].yi; - - if (star[f].y < 0) - { - star[f].x = rand() % COLS; - star[f].y = LINES - 1; - - do - { - star[f].yi = DRand() * 2; - } while(star[f].yi == 0); - } - } - - for(f = 0; f < NO_STAR; f++) - { - mvaddch(star[f].y, star[f].x, '.'); - } - - for(f = 0 ; text[f]; f++) - { - int x = COLS / 2 - strlen(text[f]) / 2; - int i; - - for(i = 0; text[f][i]; i++) - { - if (text[f][i] != ' ') - { - mvaddch(f + 4, x++, text[f][i]); - } - else - { - x++; - } - } - } - - Centre(f + 7, "PRESS SPACE"); - - refresh(); - } - - nocbreak(); - cbreak(); - erase(); - - free(star); -} - -static int CanCardSitOnTop(Card card, Card on) -{ - int ret = FALSE; - - if (card.suit != NoSuit && on.suit != NoSuit) - { - if (card.value == on.value - 1) - { - switch(card.suit) - { - case Spade: - case Club: - ret = (on.suit == Heart || on.suit == Diamond); - break; - - case Heart: - case Diamond: - ret = (on.suit == Spade || on.suit == Club); - break; - - case NoSuit: - break; - } - } - } - - return ret; -} - -static int PutCardOnSuitPile(Pile *from, Pile *hearts, Pile *diamonds, - Pile *spades, Pile *clubs) -{ - Card c; - Pile *p; - int move = FALSE; - - c = TopOfPile(from); - - if (c.suit != NoSuit) - { - switch(c.suit) - { - case Heart: - p = hearts; - break; - case Diamond: - p = diamonds; - break; - case Spade: - p = spades; - break; - case Club: - p = clubs; - break; - default: - p = NULL; - break; - } - - if (p->no) - { - if (c.value == TopOfPile(p).value + 1) - { - move = TRUE; - } - } - else - { - if (c.value == 1 ) - { - move = TRUE; - } - } - - if (move) - { - MoveTopCard(from, p); - } - } - - return move; -} - -static void RevealCard(Pile *column_down, Pile *column_up) -{ - if (column_up->no == 0) - { - if (column_down->no) - { - InsertBottomOfPile(column_up, PopPile(column_down)); - } - } -} - -static void KlondikeHelp(void) -{ - static const char *text[] = - { - "Use the cursor keys to move the cursor around.", - "", - "Press RETURN to select a bunch of cards to move.", - "Press RETURN again when you are where you want to move the cards.", - "", - "Use space to draw the next set of cards.", - "", - "Use 'S' to move a card from the drawn cards to the current column.", - "", - "Use 'P' to put the current card on the suits piles.", - "If invalid it will attempt to move the card from the drawn pile.", - "", - "Press '1', '2', '3' or '4' to move the top card from the hearts, ", - "diamonds, spades or clubs suit pile respectively back onto the board.", - "", - "Press 'Q' to quit. You'll be asked if you want to see the cards.", - "", - "", - "Press SPACE to continue.", - NULL - }; - - int y = 2; - int f; - - erase(); - - for(f = 0; text[f]; f++) - { - Centre(y++, text[f]); - } - - refresh(); - - while(getch() != ' '); -} - -static void Klondike(int draw) -{ - Deck deck; - Pile pile = {0}; - Pile turned = {0}; - Pile hearts = {0}; - Pile diamonds = {0}; - Pile clubs = {0}; - Pile spades = {0}; - Pile column_down[7] = {{0}}; - Pile column_up[7] = {{0}}; - int f; - int n; - int i; - int won = FALSE; - int quit = FALSE; - int show = FALSE; - int pos_x = 0; - int pos_y = 0; - int move_pos_x = -1; - int move_pos_y = -1; - - Init(deck); - Shuffle(deck); - - i = 0; - - for(f = 0; f < 7; f++) - { - for(n = 0; n < f; n++) - { - AddToPile(&column_down[f], deck[i++]); - } - - AddToPile(&column_up[f], deck[i++]); - } - - while(i < 52) - { - AddToPile(&pile, deck[i++]); - } - - while(!quit && !won) - { - Card c = {0}; - int key = 0; - - erase(); - - DrawCard(0, 0, TRUE, TopOfPile(&pile)); - DrawCard(0, 4, FALSE, TopOfPile(&turned)); - - DrawCard(0, 10, FALSE, TopOfPile(&hearts)); - DrawCard(0, 15, FALSE, TopOfPile(&diamonds)); - DrawCard(0, 20, FALSE, TopOfPile(&spades)); - DrawCard(0, 25, FALSE, TopOfPile(&clubs)); - - for(f = 0; f < 7; f++) - { - for(n = 0; n < column_down[f].no; n++) - { - DrawCard(2 + n, 1 + f * 5, TRUE, column_down[f].card[n]); - } - - for(n = 0; n < column_up[f].no; n++) - { - DrawCard(2 + column_down[f].no + n, 1 + f * 5, - FALSE, column_up[f].card[n]); - } - } - - if (move_pos_x != -1) - { - mvaddch(2 + column_down[move_pos_x].no + move_pos_y, - move_pos_x * 5, '*'); - mvaddch(2 + column_down[move_pos_x].no + move_pos_y, - move_pos_x * 5 + 4, '*'); - } - - if (!column_up[pos_x].no && !column_down[pos_x].no) - { - mvaddch(2, pos_x * 5, '>'); - mvaddch(2, pos_x * 5 + 4, '<'); - } - else - { - mvaddch(2 + column_down[pos_x].no + pos_y, pos_x * 5, '>'); - mvaddch(2 + column_down[pos_x].no + pos_y, pos_x * 5 + 4, '<'); - } - - if (hearts.no == 13 && diamonds.no == 13 && - spades.no == 13 && clubs.no == 13) - { - won = TRUE; - } - - refresh(); - - if (!won) - { - key = getch(); - } - - switch(key) - { - case ' ': - if (pile.no) - { - for(f = 0; f < draw; f++) - { - MoveTopCard(&pile, &turned); - } - } - else - { - SwapPile(&pile, &turned); - } - break; - - case 'S': - case 's': - c = TopOfPile(&turned); - - if (c.value == 13 && column_up[pos_x].no == 0 && - column_down[pos_x].no == 0) - { - MoveTopCard(&turned, &column_up[pos_x]); - } - else - { - if (CanCardSitOnTop(c, TopOfPile(&column_up[pos_x]))) - { - MoveTopCard(&turned, &column_up[pos_x]); - pos_y++; - } - } - break; - - case 'P': - case 'p': - if (PutCardOnSuitPile(&column_up[pos_x], - &hearts, &diamonds, &spades, &clubs)) - { - RevealCard(&column_down[pos_x], &column_up[pos_x]); - - pos_y--; - - if (pos_y < 0) - { - pos_y = 0; - } - } - else - { - PutCardOnSuitPile(&turned, &hearts, &diamonds, - &spades, &clubs); - } - break; - - case 'Q': - case 'q': - { - int k; - - quit = TRUE; - Centre(LINES-1, "Show cards?"); - refresh(); - k = getch(); - show = (k == 'y' || k == 'Y'); - break; - } - - case 10: - if (move_pos_x == -1) - { - move_pos_x = pos_x; - move_pos_y = pos_y; - } - else - { - int can_move = FALSE; - - if (column_up[move_pos_x].no && - column_up[move_pos_x].card[move_pos_y].value == 13 && - column_up[pos_x].no == 0 && column_down[pos_x].no == 0) - { - can_move = TRUE; - } - else - { - if (column_up[move_pos_x].no && - CanCardSitOnTop - (column_up[move_pos_x].card[move_pos_y], - TopOfPile(&column_up[pos_x]))) - { - can_move = TRUE; - } - } - - if (can_move) - { - Pile temp = {0}; - - while(column_up[move_pos_x].no > move_pos_y) - { - MoveTopCard(&column_up[move_pos_x], - &temp); - } - - while(temp.no) - { - MoveTopCard(&temp, - &column_up[pos_x]); - } - - RevealCard(&column_down[move_pos_x], - &column_up[move_pos_x]); - } - - move_pos_x = -1; - move_pos_y = -1; - } - break; - - case '1': - if (CanCardSitOnTop(TopOfPile(&hearts), - TopOfPile(&column_up[pos_x]))) - { - MoveTopCard(&hearts, &column_up[pos_x]); - } - break; - - case '2': - if (CanCardSitOnTop(TopOfPile(&diamonds), - TopOfPile(&column_up[pos_x]))) - { - MoveTopCard(&diamonds, &column_up[pos_x]); - } - break; - - case '3': - if (CanCardSitOnTop(TopOfPile(&spades), - TopOfPile(&column_up[pos_x]))) - { - MoveTopCard(&spades, &column_up[pos_x]); - } - break; - - case '4': - if (CanCardSitOnTop(TopOfPile(&clubs), - TopOfPile(&column_up[pos_x]))) - { - MoveTopCard(&clubs, &column_up[pos_x]); - } - break; - - case KEY_LEFT: - if (pos_x) - { - pos_x--; - pos_y = column_up[pos_x].no - 1; - - if (pos_y < 0) - { - pos_y = 0; - } - } - break; - - case KEY_RIGHT: - if (pos_x < 6) - { - pos_x++; - pos_y = column_up[pos_x].no - 1; - - if (pos_y < 0) - { - pos_y = 0; - } - } - break; - - case KEY_UP: - if (pos_y) - { - pos_y--; - } - break; - - case KEY_DOWN: - if (pos_y < column_up[pos_x].no - 1) - { - pos_y++; - } - break; - - case '?': - KlondikeHelp(); - break; - - default: - break; - } - } - - if (show) - { - for(f = 0; f < 7; f++) - { - for(n = 0; n < column_down[f].no; n++) - { - DrawCard(2 + n, 1 + f * 5, FALSE, column_down[f].card[n]); - } - - for(n = 0; n < column_up[f].no; n++) - { - DrawCard(2 + column_down[f].no + n, 1 + f * 5, - FALSE, column_up[f].card[n]); - } - } - - Centre(LINES - 1, "Press any key"); - refresh(); - getch(); - } - - if (won) - { - WinScreen(); - } - - FreePile(&pile); - FreePile(&turned); - FreePile(&hearts); - FreePile(&diamonds); - FreePile(&clubs); - FreePile(&spades); - for(f = 0; f < 7; f++) - { - FreePile(&column_down[f]); - FreePile(&column_up[f]); - } -} +#include "deck.h" +#include "pile.h" +#include "util.h" +#include "klondike.h" int main(int argc, char *argv[]) { @@ -0,0 +1,68 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + The base card types + +*/ +#include <stdlib.h> + +#include "deck.h" + +void Init(Deck deck) +{ + int i = 0; + int f; + + for(f=1; f < 14; f++) + { + deck[i].suit = Heart; + deck[i++].value = f; + + deck[i].suit = Diamond; + deck[i++].value = f; + + deck[i].suit = Club; + deck[i++].value = f; + + deck[i].suit = Spade; + deck[i++].value = f; + } +} + +void Shuffle(Deck deck) +{ + int f; + + for(f = 0; f < 1000; f++) + { + int c1 = rand() % 52; + int c2 = rand() % 52; + Card c; + + c = deck[c1]; + deck[c1] = deck[c2]; + deck[c2] = c; + } +} + +/* +vim: ai sw=4 ts=8 expandtab +*/ @@ -0,0 +1,53 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + The base card types + +*/ +#ifndef CSOL_DECK_H + +#define CSOL_DECK_H + +typedef enum +{ + NoSuit = 0, + Heart = 'H', + Diamond = 'D', + Club = 'C', + Spade = 'S' +} Suit; + +typedef struct +{ + int value; + Suit suit; +} Card; + +typedef Card Deck[52]; + +void Init(Deck deck); +void Shuffle(Deck deck); + +#endif + +/* +vim: ai sw=4 ts=8 expandtab +*/ diff --git a/klondike.c b/klondike.c new file mode 100644 index 0000000..0a5b760 --- /dev/null +++ b/klondike.c @@ -0,0 +1,513 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Klondike + +*/ +#include <curses.h> + +#include "deck.h" +#include "pile.h" +#include "util.h" + +static int CanCardSitOnTop(Card card, Card on) +{ + int ret = FALSE; + + if (card.suit != NoSuit && on.suit != NoSuit) + { + if (card.value == on.value - 1) + { + switch(card.suit) + { + case Spade: + case Club: + ret = (on.suit == Heart || on.suit == Diamond); + break; + + case Heart: + case Diamond: + ret = (on.suit == Spade || on.suit == Club); + break; + + case NoSuit: + break; + } + } + } + + return ret; +} + +static int PutCardOnSuitPile(Pile *from, Pile *hearts, Pile *diamonds, + Pile *spades, Pile *clubs) +{ + Card c; + Pile *p; + int move = FALSE; + + c = TopOfPile(from); + + if (c.suit != NoSuit) + { + switch(c.suit) + { + case Heart: + p = hearts; + break; + case Diamond: + p = diamonds; + break; + case Spade: + p = spades; + break; + case Club: + p = clubs; + break; + default: + p = NULL; + break; + } + + if (p->no) + { + if (c.value == TopOfPile(p).value + 1) + { + move = TRUE; + } + } + else + { + if (c.value == 1 ) + { + move = TRUE; + } + } + + if (move) + { + MoveTopCard(from, p); + } + } + + return move; +} + +static void RevealCard(Pile *column_down, Pile *column_up) +{ + if (column_up->no == 0) + { + if (column_down->no) + { + InsertBottomOfPile(column_up, PopPile(column_down)); + } + } +} + +static void KlondikeHelp(void) +{ + static const char *text[] = + { + "Use the cursor keys to move the cursor around.", + "", + "Press RETURN to select a bunch of cards to move.", + "Press RETURN again when you are where you want to move the cards.", + "", + "Use space to draw the next set of cards.", + "", + "Use 'S' to move a card from the drawn cards to the current column.", + "", + "Use 'P' to put the current card on the suits piles.", + "If invalid it will attempt to move the card from the drawn pile.", + "", + "Press '1', '2', '3' or '4' to move the top card from the hearts, ", + "diamonds, spades or clubs suit pile respectively back onto the board.", + "", + "Press 'Q' to quit. You'll be asked if you want to see the cards.", + "", + "", + "Press SPACE to continue.", + NULL + }; + + int y = 2; + int f; + + erase(); + + for(f = 0; text[f]; f++) + { + Centre(y++, text[f]); + } + + refresh(); + + while(getch() != ' '); +} + +void Klondike(int draw) +{ + Deck deck; + Pile pile = {0}; + Pile turned = {0}; + Pile hearts = {0}; + Pile diamonds = {0}; + Pile clubs = {0}; + Pile spades = {0}; + Pile column_down[7] = {{0}}; + Pile column_up[7] = {{0}}; + int f; + int n; + int i; + int won = FALSE; + int quit = FALSE; + int show = FALSE; + int pos_x = 0; + int pos_y = 0; + int move_pos_x = -1; + int move_pos_y = -1; + + Init(deck); + Shuffle(deck); + + i = 0; + + for(f = 0; f < 7; f++) + { + for(n = 0; n < f; n++) + { + AddToPile(&column_down[f], deck[i++]); + } + + AddToPile(&column_up[f], deck[i++]); + } + + while(i < 52) + { + AddToPile(&pile, deck[i++]); + } + + while(!quit && !won) + { + Card c = {0}; + int key = 0; + + erase(); + + DrawCard(0, 0, TRUE, TopOfPile(&pile)); + DrawCard(0, 4, FALSE, TopOfPile(&turned)); + + DrawCard(0, 10, FALSE, TopOfPile(&hearts)); + DrawCard(0, 15, FALSE, TopOfPile(&diamonds)); + DrawCard(0, 20, FALSE, TopOfPile(&spades)); + DrawCard(0, 25, FALSE, TopOfPile(&clubs)); + + for(f = 0; f < 7; f++) + { + for(n = 0; n < column_down[f].no; n++) + { + DrawCard(2 + n, 1 + f * 5, TRUE, column_down[f].card[n]); + } + + for(n = 0; n < column_up[f].no; n++) + { + DrawCard(2 + column_down[f].no + n, 1 + f * 5, + FALSE, column_up[f].card[n]); + } + } + + if (move_pos_x != -1) + { + mvaddch(2 + column_down[move_pos_x].no + move_pos_y, + move_pos_x * 5, '*'); + mvaddch(2 + column_down[move_pos_x].no + move_pos_y, + move_pos_x * 5 + 4, '*'); + } + + if (!column_up[pos_x].no && !column_down[pos_x].no) + { + mvaddch(2, pos_x * 5, '>'); + mvaddch(2, pos_x * 5 + 4, '<'); + } + else + { + mvaddch(2 + column_down[pos_x].no + pos_y, pos_x * 5, '>'); + mvaddch(2 + column_down[pos_x].no + pos_y, pos_x * 5 + 4, '<'); + } + + if (hearts.no == 13 && diamonds.no == 13 && + spades.no == 13 && clubs.no == 13) + { + won = TRUE; + } + + refresh(); + + if (!won) + { + key = getch(); + } + + switch(key) + { + case ' ': + if (pile.no) + { + for(f = 0; f < draw; f++) + { + MoveTopCard(&pile, &turned); + } + } + else + { + SwapPile(&pile, &turned); + } + break; + + case 'S': + case 's': + c = TopOfPile(&turned); + + if (c.value == 13 && column_up[pos_x].no == 0 && + column_down[pos_x].no == 0) + { + MoveTopCard(&turned, &column_up[pos_x]); + } + else + { + if (CanCardSitOnTop(c, TopOfPile(&column_up[pos_x]))) + { + MoveTopCard(&turned, &column_up[pos_x]); + pos_y++; + } + } + break; + + case 'P': + case 'p': + if (PutCardOnSuitPile(&column_up[pos_x], + &hearts, &diamonds, &spades, &clubs)) + { + RevealCard(&column_down[pos_x], &column_up[pos_x]); + + pos_y--; + + if (pos_y < 0) + { + pos_y = 0; + } + } + else + { + PutCardOnSuitPile(&turned, &hearts, &diamonds, + &spades, &clubs); + } + break; + + case 'Q': + case 'q': + { + int k; + + quit = TRUE; + Centre(LINES-1, "Show cards?"); + refresh(); + k = getch(); + show = (k == 'y' || k == 'Y'); + break; + } + + case 10: + if (move_pos_x == -1) + { + move_pos_x = pos_x; + move_pos_y = pos_y; + } + else + { + int can_move = FALSE; + + if (column_up[move_pos_x].no && + column_up[move_pos_x].card[move_pos_y].value == 13 && + column_up[pos_x].no == 0 && column_down[pos_x].no == 0) + { + can_move = TRUE; + } + else + { + if (column_up[move_pos_x].no && + CanCardSitOnTop + (column_up[move_pos_x].card[move_pos_y], + TopOfPile(&column_up[pos_x]))) + { + can_move = TRUE; + } + } + + if (can_move) + { + Pile temp = {0}; + + while(column_up[move_pos_x].no > move_pos_y) + { + MoveTopCard(&column_up[move_pos_x], + &temp); + } + + while(temp.no) + { + MoveTopCard(&temp, + &column_up[pos_x]); + } + + RevealCard(&column_down[move_pos_x], + &column_up[move_pos_x]); + } + + move_pos_x = -1; + move_pos_y = -1; + } + break; + + case '1': + if (CanCardSitOnTop(TopOfPile(&hearts), + TopOfPile(&column_up[pos_x]))) + { + MoveTopCard(&hearts, &column_up[pos_x]); + } + break; + + case '2': + if (CanCardSitOnTop(TopOfPile(&diamonds), + TopOfPile(&column_up[pos_x]))) + { + MoveTopCard(&diamonds, &column_up[pos_x]); + } + break; + + case '3': + if (CanCardSitOnTop(TopOfPile(&spades), + TopOfPile(&column_up[pos_x]))) + { + MoveTopCard(&spades, &column_up[pos_x]); + } + break; + + case '4': + if (CanCardSitOnTop(TopOfPile(&clubs), + TopOfPile(&column_up[pos_x]))) + { + MoveTopCard(&clubs, &column_up[pos_x]); + } + break; + + case KEY_LEFT: + if (pos_x) + { + pos_x--; + pos_y = column_up[pos_x].no - 1; + + if (pos_y < 0) + { + pos_y = 0; + } + } + break; + + case KEY_RIGHT: + if (pos_x < 6) + { + pos_x++; + pos_y = column_up[pos_x].no - 1; + + if (pos_y < 0) + { + pos_y = 0; + } + } + break; + + case KEY_UP: + if (pos_y) + { + pos_y--; + } + break; + + case KEY_DOWN: + if (pos_y < column_up[pos_x].no - 1) + { + pos_y++; + } + break; + + case '?': + KlondikeHelp(); + break; + + default: + break; + } + } + + if (show) + { + for(f = 0; f < 7; f++) + { + for(n = 0; n < column_down[f].no; n++) + { + DrawCard(2 + n, 1 + f * 5, FALSE, column_down[f].card[n]); + } + + for(n = 0; n < column_up[f].no; n++) + { + DrawCard(2 + column_down[f].no + n, 1 + f * 5, + FALSE, column_up[f].card[n]); + } + } + + Centre(LINES - 1, "Press any key"); + refresh(); + getch(); + } + + if (won) + { + WinScreen(); + } + + FreePile(&pile); + FreePile(&turned); + FreePile(&hearts); + FreePile(&diamonds); + FreePile(&clubs); + FreePile(&spades); + for(f = 0; f < 7; f++) + { + FreePile(&column_down[f]); + FreePile(&column_up[f]); + } +} + + +/* +vim: ai sw=4 ts=8 expandtab +*/ diff --git a/klondike.h b/klondike.h new file mode 100644 index 0000000..84d424c --- /dev/null +++ b/klondike.h @@ -0,0 +1,36 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Klondike + +*/ + +#ifndef CSOL_KLONDIKE_H + +#define CSOL_KLONDIKE_H + +void Klondike(int draw); + +#endif + +/* +vim: ai sw=4 ts=8 expandtab +*/ @@ -0,0 +1,146 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Implements a pile of cards + +*/ +#include <stdlib.h> + +#include "pile.h" +#include "util.h" + +void AddToPile(Pile *pile, Card card) +{ + pile->no++; + + pile->card = realloc(pile->card, sizeof *pile->card * pile->no); + + if (!pile->card) + { + Fatal("Failed to reallocate memory"); + } + else + { + pile->card[pile->no - 1] = card; + } +} + +void InsertBottomOfPile(Pile *pile, Card card) +{ + pile->no++; + + pile->card = realloc(pile->card, sizeof *pile->card * pile->no); + + if (!pile->card) + { + Fatal("Failed to reallocate memory"); + } + else + { + int f; + + for(f = pile->no - 1; f > 0; f--) + { + pile->card[f] = pile->card[f - 1]; + } + + pile->card[0] = card; + } +} + +Card PopPile(Pile *pile) +{ + Card c = {0}; + + if (pile->no) + { + c = pile->card[pile->no - 1]; + + pile->no--; + + if (pile->no) + { + pile->card = realloc(pile->card, sizeof *pile->card * pile->no); + + if (!pile->card) + { + Fatal("Failed to reallocate memory"); + } + } + else + { + free(pile->card); + pile->card = NULL; + } + } + else + { + free(pile->card); + pile->card = NULL; + } + + return c; +} + +Card TopOfPile(Pile *pile) +{ + Card c = {0}; + + if (pile->no) + { + c = pile->card[pile->no - 1]; + } + + return c; +} + +void MoveTopCard(Pile *from, Pile *to) +{ + Card c; + + if (from->no) + { + c = PopPile(from); + AddToPile(to, c); + } +} + +void SwapPile(Pile *a, Pile *b) +{ + Pile t; + + t = *a; + *a = *b; + *b = t; +} + +void FreePile(Pile *p) +{ + if (p->card) + { + free(p->card); + p->card = NULL; + } +} + +/* +vim: ai sw=4 ts=8 expandtab +*/ @@ -0,0 +1,50 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Implements a pile of cards + +*/ + +#ifndef CSOL_PILE_H + +#define CSOL_PILE_H + +#include "deck.h" + +typedef struct +{ + int no; + Card *card; +} Pile; + +void AddToPile(Pile *pile, Card card); +void InsertBottomOfPile(Pile *pile, Card card); +Card PopPile(Pile *pile); +Card TopOfPile(Pile *pile); +void MoveTopCard(Pile *from, Pile *to); +void SwapPile(Pile *a, Pile *b); +void FreePile(Pile *p); + +#endif + +/* +vim: ai sw=4 ts=8 expandtab +*/ @@ -0,0 +1,209 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + General purpose utilities + +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <curses.h> + +#include "util.h" + +void Fatal(const char *p) +{ + endwin(); + fprintf(stderr, "%s\n", p); + exit(EXIT_FAILURE); +} + +void Centre(int y, const char *p) +{ + size_t l = strlen(p); + + mvaddstr(y, COLS / 2 - l / 2, p); +} + +const char *CardName(Card c) +{ + static char buff[32]; + static const char *num[]= + { + "ERR", + " A", + " 2", + " 3", + " 4", + " 5", + " 6", + " 7", + " 8", + " 9", + "10", + " J", + " Q", + " K" + }; + + sprintf(buff, "%s%c", num[c.value], c.suit); + + return buff; +} + +void DrawCard(int y, int x, int face_down, Card c) +{ + if (c.suit == NoSuit) + { + mvaddstr(y, x, " "); + } + else + { + if (face_down) + { + mvaddstr(y, x, "###"); + } + else + { + if (c.suit == Spade || c.suit == Club) + { + attron(A_REVERSE); + } + + mvaddstr(y, x, CardName(c)); + + if (c.suit == Spade || c.suit == Club) + { + attroff(A_REVERSE); + } + } + } +} + +double DRand() +{ + return (double)(rand() % 1000) / 1000.0; +} + +void WinScreen() +{ + static const char *text[] = + { + "# # ## # # # # ## # #", + " # # # # # # # # # # ## #", + " # # # # # # # # # # # ##", + " # # # # # ## ## # # # #", + " # ## ## # # ## # #", + NULL, + }; + + static const int NO_STAR = 100; + + typedef struct + { + double x,y,yi; + } Star; + + Star *star; + + int f; + + star = malloc(sizeof *star * NO_STAR); + + if (!star) + { + Fatal("Memory allocation failed"); + } + + for(f = 0; f < NO_STAR; f++) + { + star[f].x = rand() % COLS; + star[f].y = rand() % LINES; + + do + { + star[f].yi = DRand() * 2; + } while(star[f].yi == 0); + } + + halfdelay(1); + + erase(); + + while(getch() != ' ') + { + for(f = 0; f < NO_STAR; f++) + { + mvaddch(star[f].y, star[f].x, ' '); + + star[f].y -= star[f].yi; + + if (star[f].y < 0) + { + star[f].x = rand() % COLS; + star[f].y = LINES - 1; + + do + { + star[f].yi = DRand() * 2; + } while(star[f].yi == 0); + } + } + + for(f = 0; f < NO_STAR; f++) + { + mvaddch(star[f].y, star[f].x, '.'); + } + + for(f = 0 ; text[f]; f++) + { + int x = COLS / 2 - strlen(text[f]) / 2; + int i; + + for(i = 0; text[f][i]; i++) + { + if (text[f][i] != ' ') + { + mvaddch(f + 4, x++, text[f][i]); + } + else + { + x++; + } + } + } + + Centre(f + 7, "PRESS SPACE"); + + refresh(); + } + + nocbreak(); + cbreak(); + erase(); + + free(star); +} + + +/* +vim: ai sw=4 ts=8 expandtab +*/ @@ -0,0 +1,43 @@ +/* + + csol - CURSES solitaire + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + General purpose utilities + +*/ + +#ifndef CSOL_UTIL_H + +#define CSOL_UTIL_H + +#include "deck.h" + +void Fatal(const char *p); +void Centre(int y, const char *p); +const char *CardName(Card c); +void DrawCard(int y, int x, int face_down, Card c); +double DRand(); +void WinScreen(); + +#endif + +/* +vim: ai sw=4 ts=8 expandtab +*/ |