Spades Tutorial Part 3

There is one last thing to do: after all four players have played a card, it’s time to figure out who took the trick, clear the play area, and reset any relevant PROP s

Declaring a winner at the end of a trick

The winner is the player who played

  • the highest card in the leading suit, if no spades (trumps) were played
  • the highest spade, if a spade was played

It’s a bit of a pain to write this logic as explained this way. Instead, we’ll do this:

  • each card in the deck has a numerical value from 1-13 for Ace-King
  • for each card that was played, assign it a point value according to what was played - if the card is the led suit, assign the card’s numerical value (but make sure that Aces are high, not low) - if the card is a spade, assign the card’s numerical value plus some value that makes sure that the lower spade beats any led suit - if the card is any other suit, just assign 0. It’s not possible for that card to win.
  • sort the players according to the point value of the card they played. The player with the highest point card wins

We’ll define a couple helper functions here, too:

# aces high
DEF get_value $card:
  ASSIGN 'tmp' INT TOKEN_GET_PROP $card 'value';
  IF EQUAL $tmp 1:
    ASSIGN 'tmp' 14;
  END_IF
  RETURN $tmp;
END_DEF

DEF spades_value $card:
  IF EQUAL get_suit $card 's':
    RETURN PLUS get_value $card 100;
  END_IF
  IF EQUAL get_suit $card GET_PROP 'suit_led':
    RETURN PLUS get_value $card 50;
  END_IF
  RETURN 0;
END_DEF

DEF tuple_helper $player_name:
  ASSIGN 'play_container' GET_CONTAINER CONCAT $player_name '_play';
  ASSIGN 'played_card' POP $play_container 0;
  RETURN TUPLE2 $player_name spades_value $played_card;
END_DEF

# figure out winner for the trick and clean up trick accounting props
DEF clean_up_trick:
  ASSIGN 'trick_values' MAP GET_PROP 'play_order' 'tuple_helper';
  DEBUG $trick_values;
  REVERSE SORT_TUPLE $trick_values;
  DEBUG $trick_values;
  ASSIGN 'winner' GET_POSITION_ELT GET_POSITION_ELT $trick_values 0 0;
  DEBUG $winner;
END_DEF
  • We defined tuple_helper so that we can take advantage of the MAP and SORT_TUPLE primitives.
  • tuple_helper takes a given player, removes their last played card from their _play container (which clears their play area), and puts together a tuple consisting of (player name, point value of the card they played)
  • MAP applies this function to all the players, creating a list of these tuples
  • SORT_TUPLE sorts this list based on the second element of the tuple (so, based on the point value)
  • As SORT_TUPLE sorts in ascending order, we apply REVERSE to make the order descending
  • Then, the first element of the list should be the winner

Misc other book keeping

There are just a few things left to do:

  • set suit_led for the first card played in a trick
  • increment current_trick_count every time a card is played, to know when the end of the trick happens
  • pass play to the next player by invoking ROTATE_PLAY_ORDER
  • actually call clean_up_trick at the end of a trick
  • clean_up_trick already clears the play area, but we should reset other registers too such as suit_led or current_trick_count
  • at the end of the trick, change the play order so that the winner leads the next trick

To do the above, we add the following code to their respective functions:

DEF clean_up_trick:
  ...
  SET_ELT GET_PROP 'player_scores' $winner PLUS GET_ELT GET_PROP 'player_scores' $winner 1;
  SET_PROP 'current_trick_count' 0;
  SET_PROP 'current_trick_num' PLUS GET_PROP 'current_trick_num' 1;
  SET_PROP 'suit_led' NULL;
  ASSIGN 'ct' 0;
  WHILE AND
    NOT EQUAL GET_POSITION_ELT GET_PROP 'play_order' 0 $winner
    LESS $ct 5:
    ROTATE_PLAY_ORDER;
    ASSIGN 'ct' PLUS $ct 1;
  END_WHILE
END_DEF

RECEIVE play_card $dummy $card_name:
  ...
  SET_PROP 'current_trick_count' PLUS GET_PROP 'current_trick_count' 1;
  IF IS_NULL GET_PROP 'suit_led':
    SET_PROP 'suit_led' get_suit $card_token;
  END_IF
  ROTATE_PLAY_ORDER;
  IF EQUAL GET_PROP 'current_trick_count' 4:
    clean_up_trick;
  END_IF
  ACCEPT;
END_RECEIVE