summaryrefslogtreecommitdiff
path: root/klondike.c
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2018-12-21 11:27:26 +0000
committerIan C <ianc@noddybox.co.uk>2018-12-21 11:27:26 +0000
commit1169992d50ce930143efe2851d02e7dfa0304e29 (patch)
treeb7c302c559b65a05f0b552244e57b2a4aed6cff2 /klondike.c
parent859dc1c2044140f7bae42705940e44b4c94bd07e (diff)
Split code up into separate objects.
Diffstat (limited to 'klondike.c')
-rw-r--r--klondike.c513
1 files changed, 513 insertions, 0 deletions
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
+*/