Name
Num players
Fa icon name
Board
-
Published
unpublished
published
Tokenset
Initial state json
{ "containers": { "draw_deck": { "props": { "name": "draw_deck", "visible_to": [], "back_visible_to_all": true }, "contents": [] }, "player_0_hand": { "props": { "player_id": 0, "visible_to": [ "player_0" ], "back_visible_to_all": true } }, "player_1_hand": { "props": { "player_id": 1, "visible_to": [ "player_1" ], "back_visible_to_all": true } }, "player_2_hand": { "props": { "player_id": 2, "visible_to": [ "player_2" ], "back_visible_to_all": true } }, "player_3_hand": { "props": { "player_id": 3, "visible_to": [ "player_3" ], "back_visible_to_all": true } }, "player_0_play": { "props": { "player_id": 0, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ], "back_visible_to_all": true } }, "player_1_play": { "props": { "player_id": 1, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ], "back_visible_to_all": true } }, "player_2_play": { "props": { "player_id": 2, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ], "back_visible_to_all": true } }, "player_3_play": { "props": { "player_id": 3, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ], "back_visible_to_all": true } }, "player_0_dealer": { "props": { "player_id": 0, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ] } }, "player_1_dealer": { "props": { "player_id": 0, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ] } }, "player_2_dealer": { "props": { "player_id": 0, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ] } }, "player_3_dealer": { "props": { "player_id": 0, "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ] } }, "last_trick": { "props": { "visible_to": [ "player_0", "player_1", "player_2", "player_3", "observer" ] } } }, "props": { "tick": 0, "bid_phase": true, "game_over": false, "winner": null, "dealer": null, "suit_led": null, "spades_broken": false, "current_trick_number": 0, "current_trick_card_count": 0, "play_order": [ "player_0", "player_1", "player_2", "player_3" ], "player_0_bid_phase": false, "player_1_bid_phase": false, "player_2_bid_phase": false, "player_3_bid_phase": false, "player_0_bid": null, "player_1_bid": null, "player_2_bid": null, "player_3_bid": null, "player_0_tricks": 0, "player_1_tricks": 0, "player_2_tricks": 0, "player_3_tricks": 0, "player_0_total_score": 0, "player_1_total_score": 0, "player_2_total_score": 0, "player_3_total_score": 0, "player_0_last_bid": null, "player_1_last_bid": null, "player_2_last_bid": null, "player_3_last_bid": null, "player_0_last_tricks": null, "player_1_last_tricks": null, "player_2_last_tricks": null, "player_3_last_tricks": null, "player_0_last_score": null, "player_1_last_score": null, "player_2_last_score": null, "player_3_last_score": null } }
Initial JSON is valid
Initial state validation message
None
Rules
DEF get_n_players: RETURN SIZE GET_PROP 'play_order'; END_DEF # set up for a new round DEF set_up_round: SET_PROP 'bid_phase' TRUE; # set up bids and trick counts for this round ASSIGN 'i' 0; WHILE LESS $i get_n_players: SET_PROP CONCAT CONCAT "player_" STR $i "_bid" NULL; SET_PROP CONCAT CONCAT "player_" STR $i "_tricks" 0; ASSIGN 'i' PLUS $i 1; END_WHILE SET_PROP 'current_trick_number' 0; # player 0, not dealer!! SET_PROP CONCAT GET_POSITION_ELT GET_PROP 'play_order' 0 '_bid_phase' TRUE; # clear out last_trick cards from last round if any WHILE NOT EQUAL SIZE GET_CONTAINER 'last_trick' 0: APPEND GET_CONTAINER 'draw_deck' POP GET_CONTAINER 'last_trick' 0; END_WHILE # shuffle deck and deal to players SHUFFLE GET_CONTAINER 'draw_deck'; ASSIGN 'i' 0; WHILE LESS $i get_n_players: ASSIGN 'j' 0; ASSIGN 'tmp_list' EMPTY_LIST; WHILE LESS $j 13: ASSIGN 'tmp' POP GET_CONTAINER 'draw_deck' 0; APPEND $tmp_list TUPLE2 $tmp sort_value $tmp; ASSIGN 'j' PLUS $j 1; END_WHILE # make player hand sorted for convenience SORT_TUPLE $tmp_list; ASSIGN 'j' 0; WHILE LESS $j 13: APPEND GET_CONTAINER CONCAT CONCAT 'player_' STR $i '_hand' GET_POSITION_ELT GET_POSITION_ELT $tmp_list $j 0; ASSIGN 'j' PLUS $j 1; END_WHILE # SET_PROP CONCAT CONCAT "player_" STR $i "_score" 0; ASSIGN 'i' PLUS $i 1; END_WHILE SET_PROP "last_trick_winner" NULL; SET_PROP "spades_broken" FALSE; END_DEF DEF set_up: INSTANTIATE_ALL_TOKENS 1 'draw_deck'; CREATE_CONTAINER 'bid_tokens'; SET_CONTAINER_VISIBILITY GET_CONTAINER 'bid_tokens' 'all'; INSTANTIATE_ALL_TOKENS 10 'bid_tokens'; # pick first dealer at random SET_PROP 'dealer' CONCAT 'player_' STR RANDOM_INT MINUS get_n_players 1; WHILE NOT EQUAL GET_POSITION_ELT GET_PROP 'play_order' MINUS get_n_players 1 GET_PROP 'dealer' : ROTATE_PLAY_ORDER; END_WHILE # SET_PROP 'current_player' GET_POSITION_ELT GET_PROP 'play_order' 0; # put dealer button into appropriate container INSTANTIATE_ALL_TOKENS 11 CONCAT GET_PROP 'dealer' '_dealer'; set_up_round; END_DEF DEF check_bid_phase_end: ASSIGN 'i' 0; WHILE LESS $i get_n_players: IF IS_NULL GET_PROP CONCAT CONCAT 'player_' STR $i '_bid': # bid phase has not ended RETURN NULL; END_IF ASSIGN 'i' PLUS $i 1; END_WHILE # bid phase has ended SET_PROP 'bid_phase' FALSE; END_DEF # bidding stuff RECEIVE select $bid_token_name: ASSIGN 'bid' TOKEN_GET_PROP GET_TOKEN $bid_token_name 'value'; IF NOT GET_PROP 'bid_phase': REJECT "It's not the bid phase right now"; END_IF IF NOT EQUAL $PLAYER_NAME $CURRENT_PLAYER: REJECT CONCAT "not player turn: " $PLAYER_NAME; END_IF # dealer cannot bid an amount that makes sum equal to 13 # exception: dealer can always bid NIL or 10-4-2 IF AND EQUAL GET_PROP 'dealer' $PLAYER_NAME NOT OR EQUAL INT $bid 0 EQUAL INT $bid 10 : ASSIGN 'current_bid_sum' 0; ASSIGN 'i' 0; WHILE LESS $i get_n_players: ASSIGN 'player_bid' GET_PROP CONCAT CONCAT 'player_' STR $i '_bid'; IF NOT IS_NULL $player_bid: ASSIGN 'current_bid_sum' PLUS $current_bid_sum $player_bid; END_IF ASSIGN 'i' PLUS $i 1; END_WHILE DEBUG $current_bid_sum; DEBUG PLUS $current_bid_sum INT $bid; DEBUG EQUAL PLUS $current_bid_sum INT $bid 13; IF EQUAL PLUS $current_bid_sum INT $bid 13: REJECT CONCAT CONCAT 'Dealer cannot bid ' STR $bid ' as it makes all bids sum to 13'; END_IF END_IF # otherwise, accept the bid SET_PROP CONCAT $PLAYER_NAME "_bid" INT $bid; SET_PROP CONCAT $PLAYER_NAME '_bid_phase' FALSE; ROTATE_PLAY_ORDER; check_bid_phase_end; # if bid phase did not end, set up next player's bid phase IF GET_PROP 'bid_phase': SET_PROP CONCAT GET_PROP 'current_player' '_bid_phase' TRUE; END_IF ACCEPT; END_RECEIVE DEF get_suit $card: RETURN TOKEN_GET_PROP $card 'suit'; END_DEF # aces high DEF get_value $card: ASSIGN 'tmp' TOKEN_GET_PROP $card 'value'; IF EQUAL $tmp 1: ASSIGN 'tmp' 14; END_IF RETURN INT $tmp; END_DEF # for sorting the hand in UI DEF sort_value $card: ASSIGN 'tmp' get_suit $card; ASSIGN 'suit_val' 0; IF EQUAL $tmp 'c': ASSIGN 'suit_val' 0; END_IF IF EQUAL $tmp 'd': ASSIGN 'suit_val' 20; END_IF IF EQUAL $tmp 'h': ASSIGN 'suit_val' 40; END_IF IF EQUAL $tmp 's': ASSIGN 'suit_val' 60; END_IF RETURN PLUS $suit_val get_value $card; END_DEF DEF follows_suit $card: RETURN EQUAL get_suit $card GET_PROP 'suit_led'; 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; # APPEND GET_CONTAINER 'draw_deck' $played_card; # RETURN TUPLE2 $player_name spades_value $played_card; # END_DEF DEF tuple_helper $player_name: ASSIGN 'play_container' GET_CONTAINER CONCAT $player_name '_play'; ASSIGN 'played_card' GET_BY_POSITION $play_container 0; RETURN TUPLE2 $player_name spades_value $played_card; END_DEF # return the delta score from this round DEF score_player $i: ASSIGN 'a' GET_PROP CONCAT CONCAT 'player_' STR $i '_bid'; ASSIGN 'b' GET_PROP CONCAT CONCAT 'player_' STR $i '_tricks'; ASSIGN 'current_score' GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score'; # special case: NIL = +/- 100 IF EQUAL $a 0: IF EQUAL $b 0: RETURN 100; END_IF RETURN -100; END_IF # special case: 10 for 200 IF EQUAL $a 10: IF GREATER_OR_EQUAL $b $a: RETURN 200; END_IF RETURN -200; END_IF # did not make the bid = - bid*10 IF LESS $b $a: RETURN MULT $a -10; END_IF # 10 * (bid - any overtrick) = 10 * (2a - b) RETURN MINUS MULT $a 20 MULT $b 10; END_DEF # round ends after 13 tricks DEF score_round: # compute scores ASSIGN 'i' 0; ASSIGN 'winner_i' 0; ASSIGN 'best_score' 0; WHILE LESS $i get_n_players: ASSIGN 'this_round_scores' score_player $i; SET_PROP CONCAT CONCAT 'player_' STR $i '_last_bid' GET_PROP CONCAT CONCAT 'player_' STR $i '_bid'; SET_PROP CONCAT CONCAT 'player_' STR $i '_last_tricks' GET_PROP CONCAT CONCAT 'player_' STR $i '_tricks'; SET_PROP CONCAT CONCAT 'player_' STR $i '_last_score' $this_round_scores; SET_PROP CONCAT CONCAT 'player_' STR $i '_total_score' PLUS GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score' $this_round_scores; IF GREATER_OR_EQUAL GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score' 250 : SET_PROP 'game_over' TRUE; END_IF IF GREATER GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score' $best_score : ASSIGN 'winner_i' $i; ASSIGN 'best_score' GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score'; END_IF ASSIGN 'i' PLUS $i 1; END_WHILE IF GET_PROP 'game_over': SET_PROP 'winner' CONCAT 'player_' STR $winner_i; END_IF 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; #SET_ELT GET_PROP 'player_scores' $winner PLUS GET_ELT GET_PROP 'player_scores' $winner 1; SET_PROP CONCAT $winner "_tricks" PLUS GET_PROP CONCAT $winner "_tricks" 1; SET_PROP "last_trick_winner" $winner; SET_PROP 'current_trick_card_count' 0; SET_PROP 'current_trick_number' PLUS GET_PROP 'current_trick_number' 1; SET_PROP 'suit_led' NULL; # move last trick back into draw deck and current trick into last trick # APPEND GET_CONTAINER 'draw_deck' $played_card; WHILE NOT EQUAL SIZE GET_CONTAINER 'last_trick' 0: APPEND GET_CONTAINER 'draw_deck' POP GET_CONTAINER 'last_trick' 0; END_WHILE ASSIGN 'i' 0; WHILE LESS $i get_n_players: ASSIGN 'play_container' GET_CONTAINER CONCAT GET_POSITION_ELT GET_PROP 'play_order' $i '_play'; APPEND GET_CONTAINER 'last_trick' POP $play_container 0; ASSIGN 'i' PLUS $i 1; END_WHILE ASSIGN 'ct' 0; WHILE AND NOT EQUAL GET_POSITION_ELT GET_PROP 'play_order' 0 $winner LESS $ct PLUS get_n_players 1: ROTATE_PLAY_ORDER; ASSIGN 'ct' PLUS $ct 1; END_WHILE IF GREATER_OR_EQUAL GET_PROP 'current_trick_number' 13: score_round; IF GET_PROP 'game_over': RETURN NULL; END_IF # shift dealer + put dealer button into next dealer's container ASSIGN 'dealer_button' POP GET_CONTAINER CONCAT GET_PROP 'dealer' '_dealer' 0; WHILE NOT EQUAL GET_POSITION_ELT GET_PROP 'play_order' MINUS get_n_players 2 GET_PROP 'dealer' : ROTATE_PLAY_ORDER; END_WHILE SET_PROP 'dealer' GET_POSITION_ELT GET_PROP 'play_order' MINUS get_n_players 1; APPEND GET_CONTAINER CONCAT GET_PROP 'dealer' '_dealer' $dealer_button; set_up_round; END_IF END_DEF # figure out how many cards there are of a given suit for a player DEF num_cards_in_hand_of_suit $player $suit: ASSIGN 'ct' 0; ASSIGN 'i' 0; ASSIGN 'player_hand' GET_CONTAINER CONCAT $player "_hand"; WHILE LESS $i SIZE $player_hand: IF EQUAL get_suit GET_BY_POSITION $player_hand $i $suit : ASSIGN 'ct' PLUS $ct 1; END_IF ASSIGN 'i' PLUS $i 1; END_WHILE RETURN $ct; END_DEF RECEIVE play_card $card_name: IF NOT EQUAL $PLAYER_NAME GET_POSITION_ELT GET_PROP 'play_order' 0: REJECT CONCAT "not player turn: " $PLAYER_NAME; END_IF IF GET_PROP 'bid_phase': REJECT "You need to bid first"; END_IF ASSIGN 'player_hand' GET_CONTAINER CONCAT $PLAYER_NAME "_hand"; IF NOT CONTAINS_BY_NAME $player_hand $card_name: REJECT CONCAT "card not in player hand: " $card_name; END_IF # can't lead spades if spades hasn't been broken # unless: player has only spades left IF AND AND AND NOT GET_PROP 'spades_broken' EQUAL GET_PROP 'current_trick_card_count' 0 EQUAL get_suit GET_TOKEN $card_name 's' NOT EQUAL num_cards_in_hand_of_suit $PLAYER_NAME 's' SIZE $player_hand : REJECT "Can't lead spades: spades hasn't been broken this round"; END_IF # get the number of cards in player hand of the led suit ASSIGN 'ct' 0; ASSIGN 'i' PLUS SIZE $player_hand -1; WHILE GREATER_OR_EQUAL $i 0: IF follows_suit GET_BY_POSITION $player_hand $i: ASSIGN 'ct' PLUS $ct 1; END_IF ASSIGN 'i' PLUS $i -1; END_WHILE IF NOT OR OR IS_NULL GET_PROP 'suit_led' follows_suit GET_TOKEN $card_name EQUAL $ct 0: REJECT CONCAT "must follow suit: " $card_name; END_IF # ok to play card ASSIGN 'player_play' GET_CONTAINER CONCAT $PLAYER_NAME '_play'; ASSIGN 'card_token' POP_BY_NAME $player_hand $card_name; IF IS_NULL $card_token: REJECT "Something went wrong"; END_IF APPEND $player_play $card_token; SET_PROP 'current_trick_card_count' PLUS GET_PROP 'current_trick_card_count' 1; IF EQUAL get_suit GET_TOKEN $card_name 's': SET_PROP 'spades_broken' TRUE; END_IF 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_card_count' get_n_players: clean_up_trick; END_IF ACCEPT; END_RECEIVE
Rules is valid
Rules validation message
[SCOPE] name='global' scope-type='None' status='closed' (No opener) [SCOPE] name='global.0-get_n_players' scope-type='DEF' status='closed' o 0: (DEF get_n_players:) 1: ( RETURN SIZE GET_PROP 'play_order';) c 2: (END_DEF) [SCOPE] name='global.3-set_up_round' scope-type='DEF' status='closed' o 5: (DEF set_up_round:) 6: ( SET_PROP 'bid_phase' TRUE;) 8: ( ASSIGN 'i' 0;) [SCOPE] name='global.3-set_up_round.6-LESS' scope-type='WHILE' status='closed' o 9: ( WHILE LESS $i get_n_players:) 10: ( SET_PROP CONCAT CONCAT "player_" STR $i "_bid" NULL;) 11: ( SET_PROP CONCAT CONCAT "player_" STR $i "_tricks" 0;) 12: ( ASSIGN 'i' PLUS $i 1;) c 13: ( END_WHILE) 14: ( SET_PROP 'current_trick_number' 0;) 16: ( SET_PROP CONCAT GET_POSITION_ELT GET_PROP 'play_order' 0 '_bid_phase' TRUE;) [SCOPE] name='global.3-set_up_round.13-NOT' scope-type='WHILE' status='closed' o 18: ( WHILE NOT EQUAL SIZE GET_CONTAINER 'last_trick' 0:) 19: ( APPEND GET_CONTAINER 'draw_deck' POP GET_CONTAINER 'last_trick' 0;) c 20: ( END_WHILE) 23: ( SHUFFLE GET_CONTAINER 'draw_deck';) 24: ( ASSIGN 'i' 0;) [SCOPE] name='global.3-set_up_round.18-LESS' scope-type='WHILE' status='closed' o 25: ( WHILE LESS $i get_n_players:) 26: ( ASSIGN 'j' 0;) 27: ( ASSIGN 'tmp_list' EMPTY_LIST;) [SCOPE] name='global.3-set_up_round.18-LESS.21-LESS' scope-type='WHILE' status='closed' o 28: ( WHILE LESS $j 13:) 29: ( ASSIGN 'tmp' POP GET_CONTAINER 'draw_deck' 0;) 30: ( APPEND $tmp_list TUPLE2 $tmp sort_value $tmp;) 31: ( ASSIGN 'j' PLUS $j 1;) c 32: ( END_WHILE) 34: ( SORT_TUPLE $tmp_list;) 35: ( ASSIGN 'j' 0;) [SCOPE] name='global.3-set_up_round.18-LESS.28-LESS' scope-type='WHILE' status='closed' o 36: ( WHILE LESS $j 13:) 37: ( APPEND GET_CONTAINER CONCAT CONCAT 'player_' STR $i '_hand' GET_POSITION_ELT GET_POSITION_ELT $tmp_list $j 0;) 38: ( ASSIGN 'j' PLUS $j 1;) c 39: ( END_WHILE) 41: ( ASSIGN 'i' PLUS $i 1;) c 42: ( END_WHILE) 43: ( SET_PROP "last_trick_winner" NULL;) 44: ( SET_PROP "spades_broken" FALSE;) c 45: (END_DEF) [SCOPE] name='global.37-set_up' scope-type='DEF' status='closed' o 47: (DEF set_up:) 48: ( INSTANTIATE_ALL_TOKENS 1 'draw_deck';) 49: ( CREATE_CONTAINER 'bid_tokens';) 50: ( SET_CONTAINER_VISIBILITY GET_CONTAINER 'bid_tokens' 'all';) 51: ( INSTANTIATE_ALL_TOKENS 10 'bid_tokens';) 54: ( SET_PROP 'dealer' CONCAT 'player_' STR RANDOM_INT MINUS get_n_players 1;) [SCOPE] name='global.37-set_up.43-NOT' scope-type='WHILE' status='closed' o 55: ( WHILE NOT EQUAL) o 56: ( GET_POSITION_ELT GET_PROP 'play_order' MINUS get_n_players 1) o 57: ( GET_PROP 'dealer') o 58: ( :) 59: ( ROTATE_PLAY_ORDER;) c 60: ( END_WHILE) 63: ( INSTANTIATE_ALL_TOKENS 11 CONCAT GET_PROP 'dealer' '_dealer';) 65: ( set_up_round;) c 66: (END_DEF) [SCOPE] name='global.49-check_bid_phase_end' scope-type='DEF' status='closed' o 68: (DEF check_bid_phase_end:) 69: ( ASSIGN 'i' 0;) [SCOPE] name='global.49-check_bid_phase_end.51-LESS' scope-type='WHILE' status='closed' o 70: ( WHILE LESS $i get_n_players:) [SCOPE] name='global.49-check_bid_phase_end.51-LESS.52-IS_NULL' scope-type='IF' status='closed' o 71: ( IF IS_NULL GET_PROP CONCAT CONCAT 'player_' STR $i '_bid':) 73: ( RETURN NULL;) c 74: ( END_IF) 75: ( ASSIGN 'i' PLUS $i 1;) c 76: ( END_WHILE) 78: ( SET_PROP 'bid_phase' FALSE;) c 79: (END_DEF) [SCOPE] name='global.59-select' scope-type='RECEIVE' status='closed' o 82: (RECEIVE select $bid_token_name:) 83: ( ASSIGN 'bid' TOKEN_GET_PROP GET_TOKEN $bid_token_name 'value';) [SCOPE] name='global.59-select.61-NOT' scope-type='IF' status='closed' o 84: ( IF NOT GET_PROP 'bid_phase':) 85: ( REJECT "It's not the bid phase right now";) c 86: ( END_IF) [SCOPE] name='global.59-select.64-NOT' scope-type='IF' status='closed' o 87: ( IF NOT EQUAL $PLAYER_NAME $CURRENT_PLAYER:) 88: ( REJECT CONCAT "not player turn: " $PLAYER_NAME;) c 89: ( END_IF) [SCOPE] name='global.59-select.67-AND' scope-type='IF' status='closed' o 92: ( IF AND) o 93: ( EQUAL GET_PROP 'dealer' $PLAYER_NAME) o 94: ( NOT OR) o 95: ( EQUAL INT $bid 0) o 96: ( EQUAL INT $bid 10) o 97: ( :) 98: ( ASSIGN 'current_bid_sum' 0;) 99: ( ASSIGN 'i' 0;) [SCOPE] name='global.59-select.67-AND.70-LESS' scope-type='WHILE' status='closed' o 100: ( WHILE LESS $i get_n_players:) 101: ( ASSIGN 'player_bid' GET_PROP CONCAT CONCAT 'player_' STR $i '_bid';) [SCOPE] name='global.59-select.67-AND.70-LESS.72-NOT' scope-type='IF' status='closed' o 102: ( IF NOT IS_NULL $player_bid:) 103: ( ASSIGN 'current_bid_sum' PLUS $current_bid_sum $player_bid;) c 104: ( END_IF) 105: ( ASSIGN 'i' PLUS $i 1;) c 106: ( END_WHILE) 107: ( DEBUG $current_bid_sum;) 108: ( DEBUG PLUS $current_bid_sum INT $bid;) 109: ( DEBUG EQUAL PLUS $current_bid_sum INT $bid 13;) [SCOPE] name='global.59-select.67-AND.80-EQUAL' scope-type='IF' status='closed' o 110: ( IF EQUAL PLUS $current_bid_sum INT $bid 13:) 111: ( REJECT CONCAT CONCAT 'Dealer cannot bid ' STR $bid ' as it makes all bids sum to 13';) c 112: ( END_IF) c 113: ( END_IF) 115: ( SET_PROP CONCAT $PLAYER_NAME "_bid" INT $bid;) 116: ( SET_PROP CONCAT $PLAYER_NAME '_bid_phase' FALSE;) 117: ( ROTATE_PLAY_ORDER;) 118: ( check_bid_phase_end;) [SCOPE] name='global.59-select.88-GET_PROP' scope-type='IF' status='closed' o 120: ( IF GET_PROP 'bid_phase':) 121: ( SET_PROP CONCAT GET_PROP 'current_player' '_bid_phase' TRUE;) c 122: ( END_IF) 123: ( ACCEPT;) c 124: (END_RECEIVE) [SCOPE] name='global.93-get_suit' scope-type='DEF' status='closed' o 126: (DEF get_suit $card:) 127: ( RETURN TOKEN_GET_PROP $card 'suit';) c 128: (END_DEF) [SCOPE] name='global.96-get_value' scope-type='DEF' status='closed' o 131: (DEF get_value $card:) 132: ( ASSIGN 'tmp' TOKEN_GET_PROP $card 'value';) [SCOPE] name='global.96-get_value.98-EQUAL' scope-type='IF' status='closed' o 133: ( IF EQUAL $tmp 1:) 134: ( ASSIGN 'tmp' 14;) c 135: ( END_IF) 136: ( RETURN INT $tmp;) c 137: (END_DEF) [SCOPE] name='global.103-sort_value' scope-type='DEF' status='closed' o 140: (DEF sort_value $card:) 141: ( ASSIGN 'tmp' get_suit $card;) 142: ( ASSIGN 'suit_val' 0;) [SCOPE] name='global.103-sort_value.106-EQUAL' scope-type='IF' status='closed' o 143: ( IF EQUAL $tmp 'c':) 144: ( ASSIGN 'suit_val' 0;) c 145: ( END_IF) [SCOPE] name='global.103-sort_value.109-EQUAL' scope-type='IF' status='closed' o 146: ( IF EQUAL $tmp 'd':) 147: ( ASSIGN 'suit_val' 20;) c 148: ( END_IF) [SCOPE] name='global.103-sort_value.112-EQUAL' scope-type='IF' status='closed' o 149: ( IF EQUAL $tmp 'h':) 150: ( ASSIGN 'suit_val' 40;) c 151: ( END_IF) [SCOPE] name='global.103-sort_value.115-EQUAL' scope-type='IF' status='closed' o 152: ( IF EQUAL $tmp 's':) 153: ( ASSIGN 'suit_val' 60;) c 154: ( END_IF) 155: ( RETURN PLUS $suit_val get_value $card;) c 156: (END_DEF) [SCOPE] name='global.120-follows_suit' scope-type='DEF' status='closed' o 158: (DEF follows_suit $card:) 159: ( RETURN EQUAL get_suit $card GET_PROP 'suit_led';) c 160: (END_DEF) [SCOPE] name='global.123-spades_value' scope-type='DEF' status='closed' o 162: (DEF spades_value $card:) [SCOPE] name='global.123-spades_value.124-EQUAL' scope-type='IF' status='closed' o 163: ( IF EQUAL get_suit $card 's':) 164: ( RETURN PLUS get_value $card 100;) c 165: ( END_IF) [SCOPE] name='global.123-spades_value.127-EQUAL' scope-type='IF' status='closed' o 166: ( IF EQUAL get_suit $card GET_PROP 'suit_led':) 167: ( RETURN PLUS get_value $card 50;) c 168: ( END_IF) 169: ( RETURN 0;) c 170: (END_DEF) [SCOPE] name='global.132-tuple_helper' scope-type='DEF' status='closed' o 179: (DEF tuple_helper $player_name:) 180: ( ASSIGN 'play_container' GET_CONTAINER CONCAT $player_name '_play';) 181: ( ASSIGN 'played_card' GET_BY_POSITION $play_container 0;) 182: ( RETURN TUPLE2 $player_name spades_value $played_card;) c 183: (END_DEF) [SCOPE] name='global.137-score_player' scope-type='DEF' status='closed' o 186: (DEF score_player $i:) 187: ( ASSIGN 'a' GET_PROP CONCAT CONCAT 'player_' STR $i '_bid';) 188: ( ASSIGN 'b' GET_PROP CONCAT CONCAT 'player_' STR $i '_tricks';) 189: ( ASSIGN 'current_score' GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score';) [SCOPE] name='global.137-score_player.141-EQUAL' scope-type='IF' status='closed' o 191: ( IF EQUAL $a 0:) [SCOPE] name='global.137-score_player.141-EQUAL.142-EQUAL' scope-type='IF' status='closed' o 192: ( IF EQUAL $b 0:) 193: ( RETURN 100;) c 194: ( END_IF) 195: ( RETURN -100;) c 196: ( END_IF) [SCOPE] name='global.137-score_player.147-EQUAL' scope-type='IF' status='closed' o 198: ( IF EQUAL $a 10:) [SCOPE] name='global.137-score_player.147-EQUAL.148-GREATER_OR_EQUAL' scope-type='IF' status='closed' o 199: ( IF GREATER_OR_EQUAL $b $a:) 200: ( RETURN 200;) c 201: ( END_IF) 202: ( RETURN -200;) c 203: ( END_IF) [SCOPE] name='global.137-score_player.153-LESS' scope-type='IF' status='closed' o 205: ( IF LESS $b $a:) 206: ( RETURN MULT $a -10;) c 207: ( END_IF) 209: ( RETURN MINUS MULT $a 20 MULT $b 10;) c 210: (END_DEF) [SCOPE] name='global.158-score_round' scope-type='DEF' status='closed' o 213: (DEF score_round:) 215: ( ASSIGN 'i' 0;) 216: ( ASSIGN 'winner_i' 0;) 217: ( ASSIGN 'best_score' 0;) [SCOPE] name='global.158-score_round.162-LESS' scope-type='WHILE' status='closed' o 218: ( WHILE LESS $i get_n_players:) 219: ( ASSIGN 'this_round_scores' score_player $i;) 220: ( SET_PROP) 221: ( CONCAT CONCAT 'player_' STR $i '_last_bid') 222: ( GET_PROP CONCAT CONCAT 'player_' STR $i '_bid';) 223: ( SET_PROP) 224: ( CONCAT CONCAT 'player_' STR $i '_last_tricks') 225: ( GET_PROP CONCAT CONCAT 'player_' STR $i '_tricks';) 226: ( SET_PROP) 227: ( CONCAT CONCAT 'player_' STR $i '_last_score') 228: ( $this_round_scores;) 229: ( SET_PROP) 230: ( CONCAT CONCAT 'player_' STR $i '_total_score') 231: ( PLUS) 232: ( GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score') 233: ( $this_round_scores;) [SCOPE] name='global.158-score_round.162-LESS.168-GREATER_OR_EQUAL' scope-type='IF' status='closed' o 234: ( IF GREATER_OR_EQUAL) o 235: ( GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score') o 236: ( 250) o 237: ( :) 238: ( SET_PROP 'game_over' TRUE;) c 239: ( END_IF) [SCOPE] name='global.158-score_round.162-LESS.171-GREATER' scope-type='IF' status='closed' o 240: ( IF GREATER) o 241: ( GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score') o 242: ( $best_score) o 243: ( :) 244: ( ASSIGN 'winner_i' $i;) 245: ( ASSIGN 'best_score' GET_PROP CONCAT CONCAT 'player_' STR $i '_total_score';) c 246: ( END_IF) 247: ( ASSIGN 'i' PLUS $i 1;) c 248: ( END_WHILE) [SCOPE] name='global.158-score_round.177-GET_PROP' scope-type='IF' status='closed' o 250: ( IF GET_PROP 'game_over':) 251: ( SET_PROP 'winner' CONCAT 'player_' STR $winner_i;) c 252: ( END_IF) c 253: (END_DEF) [SCOPE] name='global.181-clean_up_trick' scope-type='DEF' status='closed' o 256: (DEF clean_up_trick:) 257: ( ASSIGN 'trick_values' MAP GET_PROP 'play_order' 'tuple_helper';) 259: ( REVERSE SORT_TUPLE $trick_values;) 261: ( ASSIGN 'winner' GET_POSITION_ELT GET_POSITION_ELT $trick_values 0 0;) 264: ( SET_PROP CONCAT $winner "_tricks") 265: ( PLUS GET_PROP CONCAT $winner "_tricks" 1;) 266: ( SET_PROP "last_trick_winner" $winner;) 267: ( SET_PROP 'current_trick_card_count' 0;) 268: ( SET_PROP 'current_trick_number' PLUS GET_PROP 'current_trick_number' 1;) 269: ( SET_PROP 'suit_led' NULL;) [SCOPE] name='global.181-clean_up_trick.190-NOT' scope-type='WHILE' status='closed' o 272: ( WHILE NOT EQUAL SIZE GET_CONTAINER 'last_trick' 0:) 273: ( APPEND GET_CONTAINER 'draw_deck' POP GET_CONTAINER 'last_trick' 0;) c 274: ( END_WHILE) 275: ( ASSIGN 'i' 0;) [SCOPE] name='global.181-clean_up_trick.194-LESS' scope-type='WHILE' status='closed' o 276: ( WHILE LESS $i get_n_players:) 277: ( ASSIGN 'play_container' GET_CONTAINER CONCAT GET_POSITION_ELT GET_PROP 'play_order' $i '_play';) 278: ( APPEND GET_CONTAINER 'last_trick' POP $play_container 0;) 279: ( ASSIGN 'i' PLUS $i 1;) c 280: ( END_WHILE) 282: ( ASSIGN 'ct' 0;) [SCOPE] name='global.181-clean_up_trick.200-AND' scope-type='WHILE' status='closed' o 283: ( WHILE AND) o 284: ( NOT EQUAL GET_POSITION_ELT GET_PROP 'play_order' 0 $winner) o 285: ( LESS $ct PLUS get_n_players 1:) 286: ( ROTATE_PLAY_ORDER;) 287: ( ASSIGN 'ct' PLUS $ct 1;) c 288: ( END_WHILE) [SCOPE] name='global.181-clean_up_trick.204-GREATER_OR_EQUAL' scope-type='IF' status='closed' o 290: ( IF GREATER_OR_EQUAL GET_PROP 'current_trick_number' 13:) 291: ( score_round;) [SCOPE] name='global.181-clean_up_trick.204-GREATER_OR_EQUAL.206-GET_PROP' scope-type='IF' status='closed' o 292: ( IF GET_PROP 'game_over':) 293: ( RETURN NULL;) c 294: ( END_IF) 296: ( ASSIGN 'dealer_button' POP GET_CONTAINER CONCAT GET_PROP 'dealer' '_dealer' 0;) [SCOPE] name='global.181-clean_up_trick.204-GREATER_OR_EQUAL.210-NOT' scope-type='WHILE' status='closed' o 297: ( WHILE NOT EQUAL) o 298: ( GET_POSITION_ELT GET_PROP 'play_order' MINUS get_n_players 2) o 299: ( GET_PROP 'dealer') o 300: ( :) 301: ( ROTATE_PLAY_ORDER;) c 302: ( END_WHILE) 303: ( SET_PROP 'dealer' GET_POSITION_ELT GET_PROP 'play_order' MINUS get_n_players 1;) 304: ( APPEND GET_CONTAINER CONCAT GET_PROP 'dealer' '_dealer' $dealer_button;) 305: ( set_up_round;) c 306: ( END_IF) c 307: (END_DEF) [SCOPE] name='global.218-num_cards_in_hand_of_suit' scope-type='DEF' status='closed' o 310: (DEF num_cards_in_hand_of_suit $player $suit:) 311: ( ASSIGN 'ct' 0;) 312: ( ASSIGN 'i' 0;) 313: ( ASSIGN 'player_hand' GET_CONTAINER CONCAT $player "_hand";) [SCOPE] name='global.218-num_cards_in_hand_of_suit.222-LESS' scope-type='WHILE' status='closed' o 314: ( WHILE LESS $i SIZE $player_hand:) [SCOPE] name='global.218-num_cards_in_hand_of_suit.222-LESS.223-EQUAL' scope-type='IF' status='closed' o 315: ( IF EQUAL) o 316: ( get_suit GET_BY_POSITION $player_hand $i) o 317: ( $suit) o 318: ( :) 319: ( ASSIGN 'ct' PLUS $ct 1;) c 320: ( END_IF) 321: ( ASSIGN 'i' PLUS $i 1;) c 322: ( END_WHILE) 323: ( RETURN $ct;) c 324: (END_DEF) [SCOPE] name='global.230-play_card' scope-type='RECEIVE' status='closed' o 326: (RECEIVE play_card $card_name:) [SCOPE] name='global.230-play_card.231-NOT' scope-type='IF' status='closed' o 327: ( IF NOT EQUAL $PLAYER_NAME GET_POSITION_ELT GET_PROP 'play_order' 0:) 328: ( REJECT CONCAT "not player turn: " $PLAYER_NAME;) c 329: ( END_IF) [SCOPE] name='global.230-play_card.234-GET_PROP' scope-type='IF' status='closed' o 330: ( IF GET_PROP 'bid_phase':) 331: ( REJECT "You need to bid first";) c 332: ( END_IF) 333: ( ASSIGN 'player_hand' GET_CONTAINER CONCAT $PLAYER_NAME "_hand";) [SCOPE] name='global.230-play_card.238-NOT' scope-type='IF' status='closed' o 334: ( IF NOT CONTAINS_BY_NAME $player_hand $card_name:) 335: ( REJECT CONCAT "card not in player hand: " $card_name;) c 336: ( END_IF) [SCOPE] name='global.230-play_card.241-AND' scope-type='IF' status='closed' o 340: ( IF AND AND AND) o 341: ( NOT GET_PROP 'spades_broken') o 342: ( EQUAL GET_PROP 'current_trick_card_count' 0) o 343: ( EQUAL get_suit GET_TOKEN $card_name 's') o 344: ( NOT EQUAL) o 345: ( num_cards_in_hand_of_suit $PLAYER_NAME 's') o 346: ( SIZE $player_hand) o 347: ( :) 348: ( REJECT "Can't lead spades: spades hasn't been broken this round";) c 349: ( END_IF) 352: ( ASSIGN 'ct' 0;) 353: ( ASSIGN 'i' PLUS SIZE $player_hand -1;) [SCOPE] name='global.230-play_card.246-GREATER_OR_EQUAL' scope-type='WHILE' status='closed' o 354: ( WHILE GREATER_OR_EQUAL $i 0:) [SCOPE] name='global.230-play_card.246-GREATER_OR_EQUAL.247-follows_suit' scope-type='IF' status='closed' o 355: ( IF follows_suit GET_BY_POSITION $player_hand $i:) 356: ( ASSIGN 'ct' PLUS $ct 1;) c 357: ( END_IF) 358: ( ASSIGN 'i' PLUS $i -1;) c 359: ( END_WHILE) [SCOPE] name='global.230-play_card.252-NOT' scope-type='IF' status='closed' o 360: ( IF NOT) o 361: ( OR) o 362: ( OR) o 363: ( IS_NULL GET_PROP 'suit_led') o 364: ( follows_suit GET_TOKEN $card_name) o 365: ( EQUAL $ct 0:) 366: ( REJECT CONCAT "must follow suit: " $card_name;) c 367: ( END_IF) 369: ( ASSIGN 'player_play' GET_CONTAINER CONCAT $PLAYER_NAME '_play';) 370: ( ASSIGN 'card_token' POP_BY_NAME $player_hand $card_name;) [SCOPE] name='global.230-play_card.257-IS_NULL' scope-type='IF' status='closed' o 371: ( IF IS_NULL $card_token:) 372: ( REJECT "Something went wrong";) c 373: ( END_IF) 374: ( APPEND $player_play $card_token;) 375: ( SET_PROP 'current_trick_card_count' PLUS GET_PROP 'current_trick_card_count' 1;) [SCOPE] name='global.230-play_card.262-EQUAL' scope-type='IF' status='closed' o 376: ( IF EQUAL get_suit GET_TOKEN $card_name 's':) 377: ( SET_PROP 'spades_broken' TRUE;) c 378: ( END_IF) [SCOPE] name='global.230-play_card.265-IS_NULL' scope-type='IF' status='closed' o 379: ( IF IS_NULL GET_PROP 'suit_led':) 380: ( SET_PROP 'suit_led' get_suit $card_token;) c 381: ( END_IF) 382: ( ROTATE_PLAY_ORDER;) [SCOPE] name='global.230-play_card.269-EQUAL' scope-type='IF' status='closed' o 383: ( IF EQUAL GET_PROP 'current_trick_card_count' get_n_players:) 384: ( clean_up_trick;) c 385: ( END_IF) 386: ( ACCEPT;) c 387: (END_RECEIVE) (No closer)
Info Text
# Spades Spades is a trick taking game. For a detailed explanation, see [wikipedia](https://en.wikipedia.org/wiki/Spades_(card_game)) This game was built using Turnbase, an end-to-end system for specifying game logic and creating immediately playable games. For more details see the [documentation](http://turn-base.com/docs/). **NOTE** - If you accidentally or intentionally closed the bid selection modal, just refresh the page and you'll see it again. ## Summary of rules - Cutthroat - Everyone for themselves (no partnerships) - Must follow suit - If you have a card of the suit that was led, you must play it - Otherwise, you can play anything - Spades is always trump - Spades must be broken - You cannot lead spades unless it has previously been played, unless you have only spades - Bags are immediately evaluated - Any overtricks incur an immediate penalty of -10 points - E.g.: you bid 5. Taking exactly 5 tricks scores 50 points. Taking 6 tricks scores 40 (50 + -10 penalty). - Special bids - NIL = take no tricks. Succeed = +100 points, fail = -100 points - 10-4-2 = take 10 or more tricks (no penalty for over tricks). Success = +200 points, fail = -100 points - Screw the dealer - When bidding, the dealer is last to bid and cannot bid an amount that would make the sum of all bids equal to 13 - Except, nil bids and 10-4-2 are always allowed
Info Text Col 2
PUT