From 1169992d50ce930143efe2851d02e7dfa0304e29 Mon Sep 17 00:00:00 2001 From: Ian C Date: Fri, 21 Dec 2018 11:27:26 +0000 Subject: Split code up into separate objects. --- Makefile | 14 +- csol.c | 833 +------------------------------------------------------------ deck.c | 68 +++++ deck.h | 53 ++++ klondike.c | 513 +++++++++++++++++++++++++++++++++++++ klondike.h | 36 +++ pile.c | 146 +++++++++++ pile.h | 50 ++++ util.c | 209 ++++++++++++++++ util.h | 43 ++++ 10 files changed, 1133 insertions(+), 832 deletions(-) create mode 100644 deck.c create mode 100644 deck.h create mode 100644 klondike.c create mode 100644 klondike.h create mode 100644 pile.c create mode 100644 pile.h create mode 100644 util.c create mode 100644 util.h diff --git a/Makefile b/Makefile index 1002baa..2028ced 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/csol.c b/csol.c index f07e755..1abfcc9 100644 --- a/csol.c +++ b/csol.c @@ -23,840 +23,15 @@ */ #include -#include #include -#include -#include #include #include -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[]) { diff --git a/deck.c b/deck.c new file mode 100644 index 0000000..ece61a6 --- /dev/null +++ b/deck.c @@ -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 . + + ------------------------------------------------------------------------- + + The base card types + +*/ +#include + +#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 +*/ diff --git a/deck.h b/deck.h new file mode 100644 index 0000000..24d4a85 --- /dev/null +++ b/deck.h @@ -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 . + + ------------------------------------------------------------------------- + + 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 . + + ------------------------------------------------------------------------- + + Klondike + +*/ +#include + +#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 . + + ------------------------------------------------------------------------- + + Klondike + +*/ + +#ifndef CSOL_KLONDIKE_H + +#define CSOL_KLONDIKE_H + +void Klondike(int draw); + +#endif + +/* +vim: ai sw=4 ts=8 expandtab +*/ diff --git a/pile.c b/pile.c new file mode 100644 index 0000000..793cc7c --- /dev/null +++ b/pile.c @@ -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 . + + ------------------------------------------------------------------------- + + Implements a pile of cards + +*/ +#include + +#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 +*/ diff --git a/pile.h b/pile.h new file mode 100644 index 0000000..c3cfc16 --- /dev/null +++ b/pile.h @@ -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 . + + ------------------------------------------------------------------------- + + 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 +*/ diff --git a/util.c b/util.c new file mode 100644 index 0000000..92a4ba0 --- /dev/null +++ b/util.c @@ -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 . + + ------------------------------------------------------------------------- + + General purpose utilities + +*/ +#include +#include +#include +#include + +#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 +*/ diff --git a/util.h b/util.h new file mode 100644 index 0000000..cc00b85 --- /dev/null +++ b/util.h @@ -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 . + + ------------------------------------------------------------------------- + + 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 +*/ -- cgit v1.2.3