Skip to content

Commit

Permalink
Beginning
Browse files Browse the repository at this point in the history
  • Loading branch information
Blockitifluy committed Sep 1, 2024
1 parent 338739a commit 0f28070
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 0 deletions.
Binary file added Examples/sample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Better TicTacToe

This is an unlisensed project, feel free to copy, modify, seel and compile the project as stated in the [License](LICENSE) file. This is a fun little project that I made in 3 hours. This is a console based project; where two players can competively play TicTacToe. _That's it_.

![An example of the program on a console](Examples/sample.png)
<sub>An example of the program on a console</sub>

## Notes

The game doesn't actually delete `system32`; _just look at the [code](main.py)_.

This only works on a 3 by 3 grid (Add a board checking baking system).

Planning on an AI that can play TicTacToe with you.

## Credits

- _Blockitifluy_ - Me
- _You_ - ¯\\\_(ツ)\_

<sub>The cleanest code I have ever made</sub>
195 changes: 195 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
"""DON'T USE AS A MODULE"""
import re as regex

print(f"Running on {__name__}")

GRID_SIZE: int = 3
GRID_DATA: dict[tuple[int, int], int] = {}
LOCATION_REGEX: str = r"(\d+)x(\d+)"

CHECK_MATRIX: list[list[tuple[int, int]]] = [
[ (0, 0), (1, 0), (2, 0) ], # ROW 1
[ (0, 1), (1, 1), (2, 1) ], # ROW 2
[ (0, 2), (1, 2), (2, 2) ], # ROW 3

[ (0, 0), (0, 1), (0, 2) ], # COL 1
[ (1, 0), (1, 1), (1, 2) ], # COL 2
[ (2, 0), (2, 1), (2, 2) ], # COL 3

[ (0, 0), (1, 1), (2, 2) ], # DIAGONAL
[ (2, 0), (1, 1), (0, 2) ], # ANTI DIAGONAL
]

PLAYER_KEYS: dict[int, str] = {
0: "O",
1: "X"
}

# TicTacToe Board
# O | X | O 0 0
# ---|---|--- ~ 1
# X | O | X 1 2
# ---|---|--- ~ 3
# O | X | X 2 4

def generate_board_line() -> str:
"""Generates the board's board for a row
Returns
str: The board line
"""
buffer: str = ""
chars: int = GRID_SIZE * 4
for i in range(1, chars):
is_hor = i % 4 == 0 and i != 0
buffer += "|" if is_hor else "-"
return buffer

def generate_row(y: int) -> str:
"""Generates a row (based on the `GRID_DATA`) for `render_board`
Args
y (int): The row axis
Returns:
str: A row from `GRID_DATA` represented in a string
"""
buffer: str = ""
for x in range(GRID_SIZE):
slot: int | None = GRID_DATA.get((x, y), None)
player_name: str = " " if slot is None else PLAYER_KEYS[slot]
sub_buf = f" {player_name} "
if x != GRID_SIZE - 1:
sub_buf += "|"
buffer += sub_buf
return buffer

def render_board() -> str:
"""Renders the board inside a string
Returns
str: The board represented in a string
"""
total_rows = GRID_SIZE + 2
buffer: list[str] = []
line_str: str = generate_board_line()

for row in range(0, total_rows):
is_line: bool = row % 2 == 1

if not is_line:
buffer.append(generate_row(row // 2))
else:
buffer.append(line_str)
return "\n".join(buffer)

def input_location() -> tuple[int, int]:
"""
Gets the input for the TicTacToe board.
Returns
tuple[int, int]: The grid location
"""
input_str: str = input("Pick a location, e.g. 1x1: ")
regex_match = regex.search(LOCATION_REGEX, input_str)
if regex_match is None:
print("Can't be validated, please try again...")
return input_location()

x, y = int(regex_match.group(1)) - 1, int(regex_match.group(2)) - 1
if not is_board_location_valid((x, y)):
print("This location can't be placed, please try again...")
return input_location()
return x, y

def use_check_matrix() -> int | None:
"""Uses the `CHECK_MATRIX` to check of a winner
Returns
int | None: The winner's ID
"""
winner: int | None = None

for _, check_set in enumerate(CHECK_MATRIX):
first: int | None = GRID_DATA.get(check_set[0], None)
success: bool = True
if first is None:
continue
for i in range(1, GRID_SIZE):
x, y = check_set[i]
claimed_by: int | None = GRID_DATA.get((x, y), None)
if claimed_by != first:
success = False
break
if success:
winner = first

return winner

def is_board_location_valid(at: tuple[int, int]) -> bool:
"""Checks if a location is valid. Checking:
• In range,
• Is the node taken
Args:
at (tuple[int, int]): The location of the check
Returns
bool: Is the location valid
"""
x, y = at[0], at[1]

x_in_range = 0 <= x < GRID_SIZE
y_in_range = 0 <= y < GRID_SIZE

if not x_in_range or not y_in_range:
return False

node: int | None = GRID_DATA.get(at, None)

return node is None

def award_winner(winner: int):
"""Awards the winner, with cake and coke(-a-cola)
Args:
winner (int): The winner's ID
"""
player_name: str = PLAYER_KEYS[winner]
print(f"🥤🎂 Player {player_name} won!!! 🎂🥤")

def on_draw():
"""You both lost. You don't actually lose System32.
"""
print("Deleting System32")

def game_loop():
"""The TicTacToe's boards main game loop"""
i: int = 0

while True:
current_player: int = i % 2
player_name: str = PLAYER_KEYS[current_player]

print(f"It is {player_name} turn!")
location: tuple[int, int] = input_location()

GRID_DATA[location] = current_player

print(render_board())

winner: int | None = use_check_matrix()
if winner is not None:
award_winner(winner)
break
if i + 1 >= GRID_SIZE * GRID_SIZE:
on_draw()
break
i += 1

if __name__ == "__main__":
print("Ready!")
game_loop()
input("Press Enter to Exit") # yielding the game until enter
else:
raise NameError(f"Wrong file name, {__name__}")

0 comments on commit 0f28070

Please sign in to comment.