@@ -844,6 +844,47 @@ the `computeHandValue()` function, which starts all dual-value cards (Aces) at
844844their high value and downgrades them one at a time until the total is at or
845845below the target.
846846
847+ ### Multi-Round Scoring
848+
849+ For games played over multiple rounds (like Hearts), the engine supports
850+ accumulated scoring through cumulative score variables:
851+
852+ | Builtin | Args | Returns | Description |
853+ | ---------| ------| ---------| -------------|
854+ | ` accumulate_scores() ` | 0 | effect | Adds each player's ` player_score:{i} ` to ` variables["cumulative_score_{i}"] ` |
855+ | ` get_cumulative_score(i) ` | 1 | number | Returns ` variables["cumulative_score_{i}"] ` , defaults to 0 |
856+ | ` max_cumulative_score() ` | 0 | number | Highest cumulative score across all human players |
857+ | ` min_cumulative_score() ` | 0 | number | Lowest cumulative score across all human players |
858+
859+ ** Pattern** : After ` calculate_scores() ` computes round scores, call
860+ ` accumulate_scores() ` to persist them. Use ` max_cumulative_score() ` in
861+ transition conditions to determine whether the game should end or continue.
862+
863+ ``` json
864+ {
865+ "name" : " scoring" ,
866+ "kind" : " automatic" ,
867+ "automaticSequence" : [
868+ " calculate_scores()" ,
869+ " accumulate_scores()"
870+ ],
871+ "transitions" : [
872+ { "to" : " game_over" , "when" : " max_cumulative_score() >= 100" },
873+ { "to" : " round_end" , "when" : " max_cumulative_score() < 100" }
874+ ]
875+ }
876+ ```
877+
878+ The ` reset_round() ` effect automatically preserves all ` cumulative_score_* `
879+ variables while resetting everything else to ` initialVariables ` .
880+
881+ For final winner determination with cumulative scores, reference the cumulative
882+ builtins directly in the ` winCondition ` :
883+
884+ ``` json
885+ "winCondition" : " get_cumulative_score(current_player_index) == min_cumulative_score()"
886+ ```
887+
847888---
848889
849890## 12. Visibility
@@ -1475,37 +1516,80 @@ wins the trick instead of the highest led-suit card.
14751516
14761517### Scoring
14771518
1478- For avoidance games like Hearts, score penalty points:
1519+ For avoidance games like Hearts, score penalty points per round :
14791520
14801521``` json
14811522"scoring" : {
14821523 "method" : " count_cards_by_suit(concat(\" won:\" , current_player_index), \" hearts\" ) + if(has_card_with(concat(\" won:\" , current_player_index), \" Q\" , \" spades\" ), 13, 0)" ,
1483- "winCondition" : " my_score == 0 "
1524+ "winCondition" : " get_cumulative_score(current_player_index) == min_cumulative_score() "
14841525}
14851526```
14861527
1528+ The ` method ` computes each round's penalty (hearts taken + 13 for Queen of
1529+ Spades). The ` winCondition ` uses cumulative scores so the player with the lowest
1530+ total penalty wins at game end.
1531+
14871532For point-based games like Spades, score positively based on tricks won.
14881533
14891534### Ending the Game
14901535
1491- Call ` end_game() ` in the scoring phase to properly terminate the game:
1536+ For multi-round games, separate scoring from game termination. The scoring phase
1537+ accumulates round scores and branches based on whether the target has been
1538+ reached:
14921539
14931540``` json
14941541{
14951542 "name" : " scoring" ,
14961543 "kind" : " automatic" ,
1497- "automaticSequence" : [
1498- " calculate_scores() " ,
1499- " determine_winners() " ,
1500- " end_game() "
1544+ "automaticSequence" : [" calculate_scores() " , " accumulate_scores() " ],
1545+ "transitions" : [
1546+ { "to" : " game_over " , "when" : " max_cumulative_score() >= 100 " } ,
1547+ { "to" : " round_end " , "when" : " max_cumulative_score() < 100 " }
15011548 ]
15021549}
15031550```
15041551
1552+ A dedicated ` game_over ` phase handles final winner determination:
1553+
1554+ ``` json
1555+ {
1556+ "name" : " game_over" ,
1557+ "kind" : " automatic" ,
1558+ "automaticSequence" : [" determine_winners()" , " end_game()" ],
1559+ "transitions" : []
1560+ }
1561+ ```
1562+
1563+ The ` round_end ` phase lets players start the next round. The ` reset_round() `
1564+ effect clears round state but preserves ` cumulative_score_* ` variables:
1565+
1566+ ``` json
1567+ {
1568+ "name" : " round_end" ,
1569+ "kind" : " all_players" ,
1570+ "actions" : [{
1571+ "name" : " play_again" ,
1572+ "label" : " Play Again" ,
1573+ "effect" : [" collect_all_to(draw_pile)" , " reset_round()" ]
1574+ }],
1575+ "transitions" : [{ "to" : " setup" , "when" : " continue_game" }]
1576+ }
1577+ ```
1578+
15051579The ` end_game() ` builtin transitions the game status to ` finished ` and derives
15061580the winner from ` scores[result:N] ` . The ` ResultScreen ` on the client
15071581automatically displays when the game ends.
15081582
1583+ For single-round games, combine everything in one scoring phase:
1584+
1585+ ``` json
1586+ {
1587+ "name" : " scoring" ,
1588+ "kind" : " automatic" ,
1589+ "automaticSequence" : [" calculate_scores()" , " determine_winners()" , " end_game()" ]
1590+ }
1591+ ```
1592+
15091593### Limitations
15101594
15111595- ** Follow-suit enforcement** : The engine does not yet validate individual card
@@ -1514,11 +1598,10 @@ automatically displays when the game ends.
15141598 the UI level or via future per-card conditions.
15151599- ** Card passing** : Pre-game card passing (e.g., Hearts pass phase) is not yet
15161600 supported. Requires a new action type for selecting multiple cards to pass.
1517- - ** Multi-round scoring** : Accumulated scoring across rounds (e.g., Hearts to
1518- 100 ) is not yet supported (G9). Currently each game is a single round.
15191601
15201602### Complete Hearts Example
15211603
15221604See [ ` rulesets/hearts.cardgame.json ` ] ( ../rulesets/hearts.cardgame.json ) for a
15231605full working implementation. Hearts exercises: 4-player trick-taking, penalty
1524- scoring, hearts-broken tracking, and the ` end_game() ` lifecycle.
1606+ scoring, hearts-broken tracking, multi-round accumulated scoring to 100 points,
1607+ and the ` game_over ` → ` end_game() ` lifecycle.
0 commit comments