[Home]MoonShadow/Klondike

ec2-3-144-212-145.us-east-2.compute.amazonaws.com | ToothyWiki | MoonShadow | RecentChanges | Login | Webcomic

[Play the game]

Click on the bottom card in a column to turn it face-up. Click on the deck to turn three cards over and see the top one of those; once you run out of deck, you can click on where the deck was to put all the unused cards back into it. Remove cards by clicking on them to select them, then clicking on one of the four boxes down the bottom; an ace can go into any empty box, and after that you follow suit and build up in rank. In general, you move cards around by clicking on them to select them, then clicking on a legal place to move them to (the valid targets, including the foundations, become light blue whenever a card is selected). You can move a group of cards (if it's legal - that is, if they go down in value and alternate in colour) by selecting the top one and clicking on a valid destination. - MoonShadow

ToothyGDL


logic
{
  array deck
  { 
        1, 2, 3, 4, 5, 6, 7, 8, 9,
    10,11,12,13,14,15,16,17,18,19,
    20,21,22,23,24,25,26,27,28,29,
    30,31,32,33,34,35,36,37,38,39,
    40,41,42,43,44,45,46,47,48,49,
    50,51,52
  }
  int cards_in_deck { 52 }  
  int _save_slot { 0 }
  
  array waste
  { 
        1, 2, 3, 4, 5, 6, 7, 8, 9,
    10,11,12,13,14,15,16,17,18,19,
    20,21,22,23,24,25,26,27,28,29,
    30,31,32,33,34,35,36,37,38,39,
    40,41,42,43,44,45,46,47,48,49,
    50,51,52
  }  
  int cards_in_waste { 0 }
    
  /* we only need to know what the top card in each foundation is */    
  array foundation { 0, 0, 0, 0 }
  int number_of_foundations { 4 }
  
  /*
   * The longest tableau column starts with 7 face-down cards.
   * One is turned face-up by the player; then at worst
   * we can move an entire suit - 12 more cards - onto it.
   * Therefore, the maximum length of a column is 18 cards.
   */
  int tableau_column_length { 18 }
  int number_of_columns { 6 }
  int cards_to_flip { 3 }
  
  array tableau { 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  }
  
  array tableau_length { 0, 0, 0, 0, 0, 0, 0 }

  int scratch { 0 }
  int scratch2 { 0 }
  int last_dealt { 0 }  
  int started { 0 }
  int selected { -1 }
  int selected_row { 0 }
  int selected_column { 0 }  
  
  // The player starts the game by dealing from the deck to the tableau
  when { started == 0 }
  allow
  {
    // shuffle the deck
    for card ( 1..cards_in_deck )
    {
      scratch = random[card]; 
      last_dealt = deck[scratch];
      deck[scratch] = deck[card-1];
      deck[card-1] = last_dealt;
    }
    
    // deal out the tableau
    for column ( 0..number_of_columns )
    {
      for row ( 0..column )
      {
        /* deal a card */ 
        cards_in_deck--; 
        last_dealt = deck[cards_in_deck];
        
        /* place the dealt card in tableau, face down */
        tableau[ (column * tableau_column_length) + row ] = 0 - last_dealt;
        tableau_length[ column ]++;
      }
    }    
    started = 1;
  } label { "start game" }

  // Any time after the start of the game, if there are enough cards in the deck
  // the player may flip them
  when { (started == 1) and (cards_in_deck > 0) }
  allow
  {
    scratch = cards_to_flip;
    if( cards_in_deck < scratch )
    {
      scratch = cards_in_deck;
    }
    
    for i ( 1..scratch )
    {
        /* deal a card */ 
        cards_in_deck--; 
        last_dealt = deck[cards_in_deck];
        
        /* add the dealt card to the waste */
        waste[ cards_in_waste ] = last_dealt;        
        cards_in_waste++;
    }
  } label { "flip" }

  // On the other hand, if there are no cards left at all,
  // we always have the option of adding the waste back to the deck
  when { (started == 1) and (cards_in_deck == 0) }
  allow
  { 
    for i ( 1..cards_in_waste )
    {
        deck[i-1] = waste[cards_in_waste - i];
    }
    cards_in_deck = cards_in_waste;
    cards_in_waste = 0;
  } label { "flip" }
  
  // Any time after the start of the game
  // any face-down card at the bottom of its column may be turned face-up
  for column ( 0..number_of_columns )
  {
    for row ( 0..tableau_length[column] )
    {
      when 
      { 
        (started == 1) 
        and (row == (tableau_length[column] - 1))
        and (tableau[ (column * tableau_column_length) + row ] < 0)
      }
      allow
      {
        tableau[ (column * tableau_column_length) + row ] = 
            0 - tableau[ (column * tableau_column_length) + row ];
      }
      label { "Turn face-up " ( (column * tableau_column_length) + row ) }
    }
  }
  
  // Any time after the start of the game that no cards are selected,
  // any face-up card in the tableau is potentially a candidate for selection
  for column ( 0..number_of_columns )
  {
    for row ( 0..tableau_length[column] )
    {
      when 
      { 
        (
          ((selected == -1) and (row < tableau_length[column]))
          and (tableau[ (column * tableau_column_length) + row ] > 0)
        )
        and (started == 1)
      }
      allow
      {
        selected = ((column * tableau_column_length) + row);
        selected_row = row;
        selected_column = column;

        // actually, the card can only be selected if it and all cards below it are
        // in sequence down by alternate colour          
        for i ( 1..( tableau_length[column]- ( row+1 ) ) )
        {
           if
           (
              (
                (((tableau[ selected + i]-1) / 26) ^
                ((tableau[ selected ]-1) / 26))
                != ( i & 1 )
              )
              or
              (
               ( ((tableau[ selected ]-1) % 13) -
               ((tableau[ selected + i ]-1) % 13))
               != i
              )
            )
            {
              selected = -1; // wasn't supposed to do that after all
            }
        }
      }
      label { "Select "  ((column * tableau_column_length) + row) }
    }
      
    // Whenever there is a selected card, and it can legally be moved on top of
    // another card in the tableau, allow the user to do so
    when 
    { 
      ((selected > -1) and (tableau_length[column] > 0)) // special rule below will catch king moves
      and 
      (
        (
          (
            (((tableau[selected]-1) / 26) != 
              ((tableau[ (column * tableau_column_length) + (tableau_length[column]-1) ]-1) / 26))
          )
        )
        and
        (
          (((tableau[selected]-1) % 13) == 
            (((tableau[ (column * tableau_column_length) + (tableau_length[column]-1) ]-1) % 13)-1))
        )
      )
    }

    allow
    {
      // This language needs local variables, functions, postincrement..
      scratch = (column * tableau_column_length) + tableau_length[column];
      scratch2 = selected;

      for i 
        ( selected_row .. (tableau_length[selected_column]-1) )
      {
        tableau[ scratch ] = tableau[ scratch2 ];
        tableau[ scratch2 ] = 0;
        scratch++;
        scratch2++;
      }

      tableau_length[column] += (scratch2 - selected);
      tableau_length[selected_column] -= (scratch2 - selected);

      selected = -1;
    }
    label { "Move "  ((column * tableau_column_length) + (tableau_length[column])) }

    // Also check the top card in the waste
    when 
    { 
      (selected == -2)
      and 
      (
        (
          (
            ( tableau_length[column] > 0 )
            and
            (
              (
                (((waste[cards_in_waste-1]-1) / 26) != 
                  ((tableau[ (column * tableau_column_length) + (tableau_length[column]-1) ]-1) / 26))
              )
              and
              (
                (((waste[cards_in_waste-1]-1) % 13) == 
                  (((tableau[ (column * tableau_column_length) + (tableau_length[column]-1) ]-1) % 13)-1))
              )
            )
          )
        )
        or
        (
          (((waste[cards_in_waste-1]-1) % 13) == 12)
          and
          ( tableau_length[column] == 0 )
        )
      )
    }
    allow
    {
      scratch = (column * tableau_column_length) + tableau_length[column];
      cards_in_waste--;
      tableau[ scratch ] = waste[cards_in_waste];      
      tableau_length[column]++;
      selected = -1;
    }
    label { "Move "  ((column * tableau_column_length) + (tableau_length[column])) }
    
    // If selected card is a king, and there is an empty row, it can move there.
    when 
    { 
      (( selected > -1 ) and ( ((tableau[ selected ]-1)%13) == 12 ))
      and 
      ( tableau_length[column] == 0 )
    }
    allow
    {
      // This language needs local variables, functions, postincrement..
      scratch = (column * tableau_column_length) + tableau_length[column];
      scratch2 = selected;

      for i 
        ( selected_row .. (tableau_length[selected_column]-1) )
      {
        tableau[ scratch ] = tableau[ scratch2 ];
        tableau[ scratch2 ] = 0;
        scratch++;
        scratch2++;
      }

      tableau_length[column] += (scratch2 - selected);
      tableau_length[selected_column] -= (scratch2 - selected);

      selected = -1;
    }
    label { "Move "  ((column * tableau_column_length) + (tableau_length[column])) }  
  }
  
  // If selected card is the last card in the row, or in the waste,
  // see if we can move it to a foundation      
  for f ( 0..(number_of_foundations-1))
  {
    when 
    { 
      (
        (( selected > -1 ) 
          and (selected_row == (tableau_length[selected_column]-1)))
        and
        ( 
          (  (foundation[f] == 0) and (((tableau[ selected ]-1)%13) == 0)  )
          or
          (
            (((foundation[f]-1) / 13) == ((tableau[ selected ]-1)/ 13))
            and (((foundation[f]-1)%13) == (((tableau[ selected ]-1)%13) - 1 ))
          )
        )
      )
      or
      (
        ( selected == -2 ) 
        and 
        (
          (  (foundation[f] == 0) and (((waste[cards_in_waste-1]-1)%13) == 0)  )
          or
          (
            (((foundation[f]-1) / 13) == ((waste[cards_in_waste-1]-1)/ 13))
            and (((foundation[f]-1)%13) == (((waste[cards_in_waste-1]-1)%13) - 1 ))
          )
        )
      )
    }
    allow
    {
      if( selected != -2 )
      {
        tableau_length[selected_column] = tableau_length[selected_column] - 1;
        foundation[f] = tableau[ selected ];
        tableau[ selected ] = 0;
      }
      if( selected == -2 )
      {
        cards_in_waste--;
        foundation[f] = waste[cards_in_waste];
      }
      selected = -1;
    }
    label { "Foundation " f }
  }
  
  // the top card in the waste can be selected
  when { (selected == -1) and (cards_in_waste > 0) }
  allow { selected = -2; }
  label { "Select waste" }

  // ..and deselected
  when { selected == -2 }
  allow { selected = -1; }
  label { "Unselect waste" }
  
  // todo: if there's not enough cards in the deck to flip, 
  // allow the waste to be reshuffled into the deck
    
  // selected cards can be unselected at any time
  when { selected != -1 } allow { selected = -1; } label { "Unselect " selected }
}


display
{
  stringtable card
  {
    " ",
    "A♦","2♦","3♦","4♦","5♦","6♦","7♦","8♦","9♦","10♦","J♦","Q♦","K♦",
    "A♥","2♥","3♥","4♥","5♥","6♥","7♥","8♥","9♥","10♥","J♥","Q♥","K♥",
    "A♣","2♣","3♣","4♣","5♣","6♣","7♣","8♣","9♣","10♣","J♣","Q♣","K♣",
    "A♠","2♠","3♠","4♠","5♠","6♠","7♠","8♠","9♠","10♠","J♠","Q♠","K♠"
  }

  box
  {
    content { "Klondike patience" }
  }

  newline

  box
  {
    if( started == 0 )
    {
      bind_action { "start game" }
      while_active 
      {
        width { 2em }
        height { 2em }
        border { 1px solid black }
        content { "Start game" }
      }
    }
  }
  
  box
  {
    if( started == 1 )
    {
      width { 8em }
      height { 2em }
      content { " " }
      bind_action { "flip" }
      
      if( cards_in_deck > 0 )
      {
        content { "## (" cards_in_deck ")"  }
      }
      border { 1px solid black }
    }
  }

  box
  {
    if( started == 1 )
    {
      width { 8em }
      height { 2em }
      content { " " }
      
      if( selected == -2 )
      {
        border { 1px solid blue }
      }
      
      if( selected != -2 )
      {
        border { 1px solid black }
      }
      
      bind_action { "Select waste" }
      bind_action { "Unselect waste" }
      
      if( cards_in_waste > 0 )
      {
        content { card[ waste[ cards_in_waste - 1 ]] " (" cards_in_waste ")"  }
        if( ((waste[ cards_in_waste - 1 ]-1) /26) == 0 )
        {
          background{ #fbb }
        }
      }
    }
  }

  newline

  for row ( 0..(tableau_column_length-1) )
  {
    for column ( 0..number_of_columns )
    {
      box
      {
        width { 2em }
        height { 2em }
        if( tableau[ (column * tableau_column_length) + row ] < 0 )
        {
          content { "##" }
          border { 1px solid black }
          bind_action { "Turn face-up " ( (column * tableau_column_length) + row ) }
        }
        if( tableau[ (column * tableau_column_length) + row ] == 0 )
        {
          content { " " } // otherwise the table cell disappears :(
          border { 1px solid white } // assume a white background
          bind_action { "Move "  ((column * tableau_column_length) + row) }
          while_active { background { #7fd } }
        }
        if( tableau[ (column * tableau_column_length) + row ] > 0 )
        {
          content { card[ tableau[ (column * tableau_column_length) + row ] ] }
          if( selected == ((column * tableau_column_length) + row) )
          {
            border { 1px solid blue }
          }
          if( selected != ((column * tableau_column_length) + row) )
          {
            border { 1px solid black }
          }
          if( ((tableau[ (column * tableau_column_length) + row ]-1) /26) == 0 )
          {
            background{ #fbb }
          }
          bind_action { "Select "  ((column * tableau_column_length) + row) }
          bind_action { "Unselect "  ((column * tableau_column_length) + row) }
        }                
      }
    }
    newline
  }

  newline
  
  for f ( 0..(number_of_foundations-1) )
  {
      box
      {
        width { 2em }
        height { 2em }
        content { card[foundation[f]] }
        border { 1px solid black }
        bind_action { "Foundation " f }
        while_active { background { #7fd } }
        if( ((foundation[f]-1) /26) == 0 )
        {
          background{ #fbb }
        }
      }
  }  
}


[Recompile this page]
[Compiler error log]

ec2-3-144-212-145.us-east-2.compute.amazonaws.com | ToothyWiki | MoonShadow | RecentChanges | Login | Webcomic
This page is read-only | View other revisions | Recently used referrers
Last edited October 14, 2005 1:31 pm (viewing revision 26, which is the newest) (diff)
Search: