Conversation
Birdmannn
left a comment
There was a problem hiding this comment.
Neat code, but too bloated and quite some overhead shenanigans. Anyways, it seems you don't understand the poker gameplay, and their edge case scenarios... You didn't even test the scenario where the betting round ended and the variables were reset for another round to start. Remember, a betting round is different from the game round.
Anyways, we are simulating gameplay, and not just mocking variables to test models' fields that have obviously changed. If you are to simulate by mocking, then you must explain the simulation using comments, though I'd still prefer you to use the abi calls for simulation.
Please check. Thank you very much @Kaylahray. Nice job.
Please pay attention to the requested changes to avoid another review overhead. Thanks.
| assert!(no_of_chips > 0, "Raise amount must be greater than zero."); | ||
| assert!(player.chips >= total_required, "You don't have enough chips to raise."); | ||
| assert!(no_of_chips > 0, "Raise must be > 0."); | ||
| assert!(player.chips >= total_required, "Insufficient chips to raise."); |
There was a problem hiding this comment.
I see good English. Welldone
| fn is_betting_round_concluded( | ||
| self: @ContractState, game_id: u64, world: @WorldStorage, | ||
| ) -> bool { | ||
| let game: Game = world.read_model(game_id); |
There was a problem hiding this comment.
You read the whole game model just to read the players value. Why not read the players "member" instead using world.read_member()
| } | ||
|
|
||
| /// @Reentrancy, @Birdmannn | ||
| fn after_play(ref self: ContractState, caller: ContractAddress) { |
There was a problem hiding this comment.
please replace all the comments you deleted in this function because I don't consider this function as one that has been finalized. And replace the Birdmannn and that of Reentrancy's.
| game.next_player = Option::Some(next_player_addr); | ||
| } else { | ||
| // If no next player, something is wrong, or round should end. | ||
| // `is_betting_round_concluded` should have caught this. |
There was a problem hiding this comment.
How would I know? Did you test it? 😂
| let mut i: u32 = 0; | ||
| while i < game_players.len() { | ||
| let player_addr = *game_players.at(i); | ||
| let player: Player = world.read_model(player_addr); | ||
|
|
||
| if player.in_round { | ||
| active_player_count += 1; | ||
| if player.current_bet > highest_bet { | ||
| highest_bet = player.current_bet; | ||
| } | ||
| } | ||
| i += 1; | ||
| }; | ||
|
|
There was a problem hiding this comment.
I don't think this would work out. I think the former implementation was just fine. after_play is called after each player has played, and checks Line588 for that particular player. You looping through all the players after each player has played just for the line588 is just gas.
| assert_eq!(player_1_updated.chips, 0, "Player 1 should have 0 chips left"); | ||
| } | ||
|
|
||
| #[test] |
There was a problem hiding this comment.
Please feel free to remove this test
| let contracts = array![CoreContract::Actions]; | ||
| let (mut world, systems) = deploy_contracts(contracts); | ||
| mock_poker_game(ref world); | ||
|
|
||
| // [Setup State] - Game with bet spacing of 20 | ||
| let mut game: Game = world.read_model(1); | ||
| game.current_bet = 100; | ||
| let mut params = game.params; | ||
| params.bet_spacing = 20; // Set bet spacing to 20 | ||
| game.params = params; | ||
| world.write_model(@game); | ||
|
|
||
| let mut player_1: Player = world.read_model(PLAYER_1()); | ||
| player_1.current_bet = 100; | ||
| player_1.chips = 2000; | ||
| world.write_model(@player_1); | ||
|
|
There was a problem hiding this comment.
as normal, that function we spoke about earlier
| // [Assert] | ||
| let player_1_updated: Player = world.read_model(PLAYER_1()); | ||
| assert_eq!( | ||
| player_1_updated.current_bet, 240, "Raise should succeed with valid bet spacing", |
There was a problem hiding this comment.
no need for this, please remove this block
| #[test] | ||
| #[should_panic( | ||
| expected: ("Raise amount must be in multiples of bet_spacing", 'ENTRYPOINT_FAILED'), | ||
| )] | ||
| fn test_bet_spacing_validation_fails_with_invalid_multiple() { | ||
| // [Setup] @kaylahray | ||
| let contracts = array![CoreContract::Actions]; | ||
| let (mut world, systems) = deploy_contracts(contracts); | ||
| mock_poker_game(ref world); | ||
|
|
||
| // [Setup State] - Game with bet spacing of 20 | ||
| let mut game: Game = world.read_model(1); | ||
| game.current_bet = 100; | ||
| let mut params = game.params; | ||
| params.bet_spacing = 20; // Set bet spacing to 20 | ||
| game.params = params; | ||
| world.write_model(@game); | ||
|
|
||
| let mut player_1: Player = world.read_model(PLAYER_1()); | ||
| player_1.current_bet = 100; | ||
| player_1.chips = 2000; | ||
| world.write_model(@player_1); | ||
|
|
||
| set_contract_address(player_1.id); | ||
|
|
There was a problem hiding this comment.
Merge this test with the previous by:
- Removing this test
- in the previous test, call Line361 with the next player
- Yes, it'll panic, so add the
should_panicmacro at the top of the test. - And yes, when panicking, please remove the 'ENTRYPOINT_FAILED' message from the macro's
expectedvalue... for all your panic tests please.
|
|
||
| // [Execute] - Player 2 checks, which should trigger betting round conclusion check | ||
| // Since all non-all-in players have equal bets, this should conclude the betting round | ||
| systems.actions.check(); |
There was a problem hiding this comment.
if player_2 checks, then it should panic, because player 2 is the highest_staker. Though I don't understand what this test actually tests... but if this test passes, then you should understand the game flow and rework the logic. But first you must understand the game flow.
Help me explain what this test does, or you can remove this test totally.
|
@Birdmannn I've implemented every change requested and returned your comment and fixed my grammar 👀🫡 |
|
|
||
| // At this point, all players should have current_bet = 40, game.current_bet = 40 | ||
| // The betting round should be complete and reset should have happened | ||
| assert!(game.highest_staker.is_none(), "Highest staker not reset"); |
There was a problem hiding this comment.
Then the reset is complete. Check the community_dealing. As per last review on the tests, and the issue description, community dealing must be tested to. After first first round of betting, deal_community_card() must go through three times.
Add test: The fourth one must panic
Add test: After adding three community cards, the betting should resume
Add test: Try any of the betting functions when the betting round has ended. This should panic.
and this just means that the betting cycle you wrote above can be extracted into a separate function called feign_betting_round() or something, and can be reused in any of the three tests as above.
There are other edge cases tests, but I'll leave it for another issue.
| } else { | ||
| // If no next player, something is wrong, or round should end. | ||
| // `is_betting_round_concluded` should have caught this. | ||
| game.showdown = true; | ||
| game.next_player = Option::None; | ||
| game.next_player = next_player_option; | ||
|
|
||
| // Check if betting round is complete (more gas efficient) @kaylahray | ||
| if self.is_betting_round_complete(@game, @world) { | ||
| // Reset betting state efficiently | ||
| self.reset_betting_round(game_id, ref game, ref world); | ||
| } | ||
| } | ||
|
|
||
| world.write_model(@game); | ||
|
|
| } | ||
| // @kaylahray Testing all-in sets highest staker | ||
| #[test] | ||
| fn test_all_in_sets_highest_staker() { |
There was a problem hiding this comment.
As per the issue description, The aim was for all_in to work regardless of the bet_spacing. So write a new test that mocks the player's balance to not be a multiple of the bet. spacing and call the all_in. It should pass
There was a problem hiding this comment.
Can you see that this block is repeated? Three good times...









Closes #193
Highest Staker Logic
Requirement
The
highest_stakershould be updated:Implementation
raise()function inactions.caironow setshighest_staker.all_in()function includes logic to updatehighest_stakeronly when the all-in amount exceedsgame.current_bet.Tests
test_raise_sets_highest_stakerConfirms that when a player successfully raises, they are correctly set as the
highest_stakerin the game state.test_all_in_sets_highest_staker_when_amount_greater_than_current_betVerifies that a player who goes all-in with an amount greater than the current table bet becomes the new
highest_staker.test_all_in_does_not_set_highest_staker_when_amount_less_than_current_betEnsures that a player who goes all-in with an amount less than the current bet does not become the
highest_staker.Resetting Values After a Betting Round
Requirement
At the end of a betting round:
current_betfor all players and the game.game.highest_stakertoNone.This must happen automatically when betting concludes and must be tested.
Implementation
is_betting_round_concludedandreset_betting_round_values. This cleanly separates the logic for checking if a round has ended (all active, non-all-in players have equal bets) and the logic for resetting the game state (player bets, game bets, highest staker) for the next phase of the round.after_play()whenis_betting_round_concluded()returnstrue.Tests
test_betting_round_concludes_when_all_active_players_have_equal_betsEnsures that when all active players have equal bets and a non-betting action (e.g.,
check) occurs, the round concludes and all values (current_bet,highest_staker) are reset.test_betting_round_does_not_conclude_when_players_have_unequal_betsA control test confirming that if players have unequal bets, the betting round does not conclude, and the game state remains unchanged.
test_betting_round_with_all_in_players_exempt_from_bet_matchingVerifies that all-in players (who can’t raise further) do not block the round from ending. It confirms that the round ends when all other active players match the highest bet.
Bet Spacing Validation
Requirement
game_params.bet_spacing.Implementation
The
raise()function includes an assertion:This ensures invalid raises are rejected at runtime.
Tests
test_bet_spacing_validation_in_raiseConfirms that a player can successfully raise when their amount is a valid multiple of
game.params.bet_spacing.test_bet_spacing_validation_fails_with_invalid_multipleEnsures that any raise not adhering to the bet spacing rule correctly panics and is not accepted.