Tracking wins

Let's start by tracking the number of wins for each player over time. We'll add a totalWins function that returns the total number of games won by address:

    function test_playerX_win_count_starts_at_zero() public {
        assertEq(ttt.totalWins(PLAYER_X), 0);
    }

    function test_playerO_win_count_starts_at_zero() public {
        assertEq(ttt.totalWins(PLAYER_O), 0);
    }

    function test_increments_win_count_on_win() public {
        playerX.markSpace(0, 0);
        playerO.markSpace(0, 3);
        playerX.markSpace(0, 1);
        playerO.markSpace(0, 4);
        playerX.markSpace(0, 2);
        assertEq(ttt.totalWins(PLAYER_X), 1);

        ttt.newGame(PLAYER_X, PLAYER_O);
        playerX.markSpace(1, 1);
        playerO.markSpace(1, 2);
        playerX.markSpace(1, 3);
        playerO.markSpace(1, 4);
        playerX.markSpace(1, 5);
        playerO.markSpace(1, 6);
        assertEq(ttt.totalWins(PLAYER_O), 1);
    }

We can store the win count using a mapping with the player's address as its key and their uint256 win count as its value. We'll rely on the default getter to create a public totalWins() function:

mapping(address => uint256) public totalWins;

Our first two tests should pass thanks to the mapping's default balue of zero, but we'll need to increment the win count in order to pass the third:

$ forge test -m win_count
Running 3 tests for src/test/TicTacToken.t.sol:TicTacTokenTest
[FAIL] test_increments_win_count_on_win() (gas: 199302)
Logs:
  Error: a == b not satisfied [uint]
    Expected: 1
      Actual: 0

[PASS] test_playerO_win_count_starts_at_zero() (gas: 7811)
[PASS] test_playerX_win_count_starts_at_zero() (gas: 7845)
Test result: FAILED. 2 passed; 1 failed; finished in 2.65ms

Failed tests:
[FAIL] test_increments_win_count_on_win() (gas: 199302)

Let's check for a winner each time we mark a space, and increment the value in the mapping when we find one:

    function markSpace(uint256 id, uint256 space) public {
        require(_validPlayer(id), "Unauthorized");
        require(_validTurn(id), "Not your turn");
        require(_emptySpace(id, space), "Already marked");
        games[id].board[space] = _getSymbol(id, msg.sender);
        games[id].turns++;
        if(winner(id) != 0) {
            address winnerAddress = _getAddress(id, winner(id));
            totalWins[winnerAddress] += 1;
        }
    }

    function _getAddress(uint256 id, uint256 symbol) internal view returns (address) {
        if (symbol == X) return games[id].playerX;
        if (symbol == O) return games[id].playerO;
        return address(0);
    }

Looking good!

$ forge test -m win_count
[⠊] Compiling...
[⠔] Compiling 2 files with 0.8.10
[⠑] Solc finished in 414.60ms
Compiler run successful

Running 3 tests for src/test/TicTacToken.t.sol:TicTacTokenTest
[PASS] test_increments_win_count_on_win() (gas: 274823)
[PASS] test_playerO_win_count_starts_at_zero() (gas: 7811)
[PASS] test_playerX_win_count_starts_at_zero() (gas: 7845)
Test result: ok. 3 passed; 0 failed; finished in 3.44ms