{"id":"more-lists-game-board","slug":"game-board","title":"A grid as a game board","text":"Many games are played on a grid: chess, tic-tac-toe, minesweeper, sudoku, battleship. A matrix is a natural way to keep such a board in a program. Each item is one square, and its two indexes are the square's row and column.\n\nHere is a small sea for a game of battleship, where `0` is water and `1` is part of a ship. The program prints it with symbols a player can read:\n\n```python\nsea = [\n    [0, 0, 0, 0, 0],\n    [0, 1, 1, 1, 0],\n    [0, 0, 0, 0, 0],\n    [1, 0, 0, 0, 0],\n    [1, 0, 0, 1, 1],\n]\n\nfor row in sea:\n    for square in row:\n        if square == 1:\n            print(\"#\", end=\"\")\n        else:\n            print(\"~\", end=\"\")\n    print()\n```\n\n```output\n~~~~~\n~###~\n~~~~~\n#~~~~\n#~~##\n```\n\n`end=\"\"` keeps the squares of a row on the same line, and the `print()` after the inner loop ends the row.\n\n### Asking the board\n\nQuestions about the game become small functions. How many ship squares are there, and does a shot at a given row and column hit?\n\n```python\ndef ship_squares(sea: list) -> int:\n    count = 0\n    for row in sea:\n        count += row.count(1)\n    return count\n\n\ndef is_hit(sea: list, row_index: int, column_index: int) -> bool:\n    return sea[row_index][column_index] == 1\n\n\nsea = [\n    [0, 0, 0, 0, 0],\n    [0, 1, 1, 1, 0],\n    [0, 0, 0, 0, 0],\n    [1, 0, 0, 0, 0],\n    [1, 0, 0, 1, 1],\n]\nprint(\"Ship squares:\", ship_squares(sea))\nprint(is_hit(sea, 1, 2))\nprint(is_hit(sea, 2, 2))\n```\n\n```output\nShip squares: 7\nTrue\nFalse\n```\n\n### Strings on the board\n\nFor some games, strings say more than numbers. This tic-tac-toe board holds `\"X\"`, `\"O\"` and `\".\"` for an empty square. A row is a win when its three squares hold the same piece, and that piece isn't the empty `\".\"`:\n\n```python\ndef row_winner(board: list, row_index: int) -> str:\n    row = board[row_index]\n    if row[0] != \".\" and row[0] == row[1] and row[1] == row[2]:\n        return row[0]\n    return \"\"\n\n\nboard = [\n    [\"O\", \"X\", \".\"],\n    [\"X\", \"X\", \"X\"],\n    [\"O\", \".\", \"O\"],\n]\nprint(row_winner(board, 1))\nprint(row_winner(board, 2) == \"\")\n```\n\n```output\nX\nTrue\n```\n\nThe function returns the winning piece, or an empty string when the row isn't a win. Printing an empty string prints an empty line, so the example compares it with `\"\"` instead.\n","task":"Write a function `column_winner(board, column_index)` that does the same for a column: it returns the piece (`\"X\"` or `\"O\"`) when every square of the column holds that piece, and `\"\"` otherwise. A column of empty squares `\".\"` is not a win.\n\nFor the board `[[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]]`, column 0 gives `\"X\"` and column 1 gives `\"\"`. Your function should work for a board of any number of rows.\n","kind":"code","seed":"def column_winner(board: list, column_index: int) -> str:\n    pass\n\n\nif __name__ == \"__main__\":\n    board = [[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]]\n    print(column_winner(board, 0))\n    print(column_winner(board, 1) == \"\")\n","solution":"def column_winner(board: list, column_index: int) -> str:\n    first = board[0][column_index]\n    if first == \".\":\n        return \"\"\n    for row in board:\n        if row[column_index] != first:\n            return \"\"\n    return first\n\n\nif __name__ == \"__main__\":\n    board = [[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]]\n    print(column_winner(board, 0))\n    print(column_winner(board, 1) == \"\")\n","hints":["Take the piece in the first row of the column, `board[0][column_index]`. If it's `\".\"`, the column can't be a win.","Then go through the rows: as soon as one row has something else at `column_index`, return `\"\"`.","If the loop finishes, every square matched the first one, so return that piece."],"tests":[{"name":"column_winner([[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]], 0)","stdin":"","call":"column_winner([[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]], 0)\n","expect":"'X'","hidden":false,"returns":true},{"name":"column_winner([[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]], 1)","stdin":"","call":"column_winner([[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]], 1)\n","expect":"''","hidden":false,"returns":true},{"name":"column_winner([[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]], 2)","stdin":"","call":"column_winner([[\"X\", \"O\", \".\"], [\"X\", \"O\", \"X\"], [\"X\", \".\", \"O\"]], 2)\n","expect":"''","hidden":true,"returns":true},{"name":"column_winner([[\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"]], 0)","stdin":"","call":"column_winner([[\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"]], 0)\n","expect":"''","hidden":true,"returns":true},{"name":"column_winner([[\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"]], 1)","stdin":"","call":"column_winner([[\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"], [\".\", \"O\"]], 1)\n","expect":"'O'","hidden":true,"returns":true},{"name":"column_winner([[\"X\", \".\"], [\"X\", \"O\"], [\"X\", \"O\"], [\"O\", \"O\"]], 0)","stdin":"","call":"column_winner([[\"X\", \".\"], [\"X\", \"O\"], [\"X\", \"O\"], [\"O\", \"O\"]], 0)\n","expect":"''","hidden":true,"returns":true},{"name":"column_winner([[\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \".\"]], 0)","stdin":"","call":"column_winner([[\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \".\"]], 0)\n","expect":"'O'","hidden":true,"returns":true},{"name":"column_winner([[\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \".\"]], 1)","stdin":"","call":"column_winner([[\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \"X\"], [\"O\", \".\"]], 1)\n","expect":"''","hidden":true,"returns":true}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"board = [\n    [\"X\", \"O\", \"X\"],\n    [\".\", \"X\", \"O\"],\n    [\"O\", \".\", \"X\"],\n]\nprint(board[2][0])\nprint(board[1])\nempty = 0\nfor row in board:\n    empty += row.count(\".\")\nprint(empty)\nprint(board[0][0] + board[1][1] + board[2][2])\n","solution":"board = [\n    [\"X\", \"O\", \"X\"],\n    [\".\", \"X\", \"O\"],\n    [\"O\", \".\", \"X\"],\n]\nprint(board[2][0])\nprint(board[1])\nempty = 0\nfor row in board:\n    empty += row.count(\".\")\nprint(empty)\nprint(board[0][0] + board[1][1] + board[2][2])\n","hints":["The first index is the row and the second the column, both from zero.","`row.count(\".\")` counts the empty squares of one row, and the loop adds up the counts of all rows.","The last line joins three strings with `+`: the squares from the top left corner to the bottom right one."],"tests":[{"name":"Output","stdin":"","expect":"O\n['.', 'X', 'O']\n2\nXXX","hidden":false}],"require":[],"forbid":[],"lines":["O","['.', 'X', 'O']","2","XXX"]},{"task":"Write a function `diagonal_winner(board)` for a 3 by 3 tic-tac-toe board. A diagonal runs from one corner through the middle square to the opposite corner, and there are two of them. If all three squares of a diagonal hold the same piece (`\"X\"` or `\"O\"`), the function returns that piece. Otherwise it returns `\"\"`. Empty squares are `\".\"`.\n\nFor example, for `[[\"X\", \"O\", \".\"], [\".\", \"X\", \"O\"], [\"O\", \".\", \"X\"]]` it returns `\"X\"`.\n","kind":"code","seed":"def diagonal_winner(board: list) -> str:\n    pass\n\n\nif __name__ == \"__main__\":\n    print(diagonal_winner([[\"X\", \"O\", \".\"], [\".\", \"X\", \"O\"], [\"O\", \".\", \"X\"]]))\n","solution":"def diagonal_winner(board: list) -> str:\n    middle = board[1][1]\n    if middle == \".\":\n        return \"\"\n    if board[0][0] == middle and board[2][2] == middle:\n        return middle\n    if board[0][2] == middle and board[2][0] == middle:\n        return middle\n    return \"\"\n\n\nif __name__ == \"__main__\":\n    print(diagonal_winner([[\"X\", \"O\", \".\"], [\".\", \"X\", \"O\"], [\"O\", \".\", \"X\"]]))\n","hints":["One diagonal is `board[0][0]`, `board[1][1]`, `board[2][2]`. The other is `board[0][2]`, `board[1][1]`, `board[2][0]`.","Both diagonals go through the middle square `board[1][1]`. If it's empty, there's no win.","Otherwise check whether both corners of one diagonal hold the same piece as the middle, then the same for the other diagonal."],"tests":[{"name":"diagonal_winner([[\"X\", \"O\", \".\"], [\".\", \"X\", \"O\"], [\"O\", \".\", \"X\"]])","stdin":"","call":"diagonal_winner([[\"X\", \"O\", \".\"], [\".\", \"X\", \"O\"], [\"O\", \".\", \"X\"]])\n","expect":"'X'","hidden":false,"returns":true},{"name":"diagonal_winner([[\"X\", \"X\", \"O\"], [\".\", \"O\", \"X\"], [\"O\", \".\", \"X\"]])","stdin":"","call":"diagonal_winner([[\"X\", \"X\", \"O\"], [\".\", \"O\", \"X\"], [\"O\", \".\", \"X\"]])\n","expect":"'O'","hidden":false,"returns":true},{"name":"diagonal_winner([[\"X\", \"X\", \"X\"], [\"O\", \"O\", \".\"], [\".\", \".\", \".\"]])","stdin":"","call":"diagonal_winner([[\"X\", \"X\", \"X\"], [\"O\", \"O\", \".\"], [\".\", \".\", \".\"]])\n","expect":"''","hidden":true,"returns":true},{"name":"diagonal_winner([[\"O\", \".\", \".\"], [\".\", \".\", \".\"], [\".\", \".\", \"O\"]])","stdin":"","call":"diagonal_winner([[\"O\", \".\", \".\"], [\".\", \".\", \".\"], [\".\", \".\", \"O\"]])\n","expect":"''","hidden":true,"returns":true},{"name":"diagonal_winner([[\"O\", \"X\", \"X\"], [\"X\", \"O\", \"O\"], [\"X\", \"O\", \"X\"]])","stdin":"","call":"diagonal_winner([[\"O\", \"X\", \"X\"], [\"X\", \"O\", \"O\"], [\"X\", \"O\", \"X\"]])\n","expect":"''","hidden":true,"returns":true}],"require":[],"forbid":[]}]}