mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-12-18 17:20:16 +00:00
285 lines
8.4 KiB
Python
285 lines
8.4 KiB
Python
|
"""
|
||
|
Matrix-Based Game Script
|
||
|
=========================
|
||
|
This script implements a matrix-based game where players interact with a grid of
|
||
|
elements. The primary goals are to:
|
||
|
- Identify connected elements of the same type from a selected position.
|
||
|
- Remove those elements, adjust the matrix by simulating gravity, and reorganize empty
|
||
|
columns.
|
||
|
- Calculate and display the score based on the number of elements removed in each move.
|
||
|
|
||
|
Functions:
|
||
|
-----------
|
||
|
1. `find_repeat`: Finds all connected elements of the same type.
|
||
|
2. `increment_score`: Calculates the score for a given move.
|
||
|
3. `move_x`: Simulates gravity in a column.
|
||
|
4. `move_y`: Reorganizes the matrix by shifting columns leftward when a column becomes
|
||
|
empty.
|
||
|
5. `play`: Executes a single move, updating the matrix and returning the score.
|
||
|
|
||
|
Input Format:
|
||
|
--------------
|
||
|
1. Matrix size (`lines`): Integer specifying the size of the matrix (N x N).
|
||
|
2. Matrix content (`matrix`): Rows of the matrix, each consisting of characters.
|
||
|
3. Number of moves (`movs`): Integer indicating the number of moves.
|
||
|
4. List of moves (`movements`): A comma-separated string of coordinates for each move.
|
||
|
|
||
|
(0,0) position starts from first left column to last right, and below row to up row
|
||
|
|
||
|
|
||
|
Example Input:
|
||
|
---------------
|
||
|
4
|
||
|
RRBG
|
||
|
RBBG
|
||
|
YYGG
|
||
|
XYGG
|
||
|
2
|
||
|
0 1,1 1
|
||
|
|
||
|
Example (0,0) = X
|
||
|
|
||
|
Output:
|
||
|
--------
|
||
|
The script outputs the total score after processing all moves.
|
||
|
|
||
|
Usage:
|
||
|
-------
|
||
|
Run the script and provide the required inputs as prompted.
|
||
|
|
||
|
"""
|
||
|
|
||
|
|
||
|
def validate_matrix_size(size: int) -> None:
|
||
|
"""
|
||
|
>>> validate_matrix_size(-1)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: Matrix size must be a positive integer.
|
||
|
"""
|
||
|
if not isinstance(size, int) or size <= 0:
|
||
|
raise ValueError("Matrix size must be a positive integer.")
|
||
|
|
||
|
|
||
|
def validate_matrix_content(matrix: list[str], size: int) -> None:
|
||
|
"""
|
||
|
Validates that the number of elements in the matrix matches the given size.
|
||
|
|
||
|
>>> validate_matrix_content(['aaaa', 'aaaa', 'aaaa', 'aaaa'], 3)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: The matrix dont match with size.
|
||
|
>>> validate_matrix_content(['aa%', 'aaa', 'aaa'], 3)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: Matrix rows can only contain letters and numbers.
|
||
|
>>> validate_matrix_content(['aaa', 'aaa', 'aaaa'], 3)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: Each row in the matrix must have exactly 3 characters.
|
||
|
"""
|
||
|
print(matrix)
|
||
|
if len(matrix) != size:
|
||
|
raise ValueError("The matrix dont match with size.")
|
||
|
for row in matrix:
|
||
|
if len(row) != size:
|
||
|
msg = f"Each row in the matrix must have exactly {size} characters."
|
||
|
raise ValueError(msg)
|
||
|
if not all(char.isalnum() for char in row):
|
||
|
raise ValueError("Matrix rows can only contain letters and numbers.")
|
||
|
|
||
|
|
||
|
def validate_moves(moves: list[tuple[int, int]], size: int) -> None:
|
||
|
"""
|
||
|
>>> validate_moves([(1, 2), (-1, 0)], 3)
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: Move is out of bounds for a matrix.
|
||
|
"""
|
||
|
for move in moves:
|
||
|
x, y = move
|
||
|
if not (0 <= x < size and 0 <= y < size):
|
||
|
raise ValueError("Move is out of bounds for a matrix.")
|
||
|
|
||
|
|
||
|
def parse_moves(input_str: str) -> list[tuple[int, int]]:
|
||
|
"""
|
||
|
>>> parse_moves("0 1, 1 1")
|
||
|
[(0, 1), (1, 1)]
|
||
|
>>> parse_moves("0 1, 1 1, 2")
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: Each move must have exactly two numbers.
|
||
|
>>> parse_moves("0 1, 1 1, 2 4 5 6")
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
ValueError: Each move must have exactly two numbers.
|
||
|
"""
|
||
|
moves = []
|
||
|
for pair in input_str.split(","):
|
||
|
parts = pair.strip().split()
|
||
|
if len(parts) != 2:
|
||
|
raise ValueError("Each move must have exactly two numbers.")
|
||
|
x, y = map(int, parts)
|
||
|
moves.append((x, y))
|
||
|
return moves
|
||
|
|
||
|
|
||
|
def find_repeat(
|
||
|
matrix_g: list[list[str]], row: int, column: int, size: int
|
||
|
) -> set[tuple[int, int]]:
|
||
|
"""
|
||
|
Finds all connected elements of the same type from a given position.
|
||
|
|
||
|
>>> find_repeat([['A', 'B', 'A'], ['A', 'B', 'A'], ['A', 'A', 'A']], 0, 0, 3)
|
||
|
{(1, 2), (2, 1), (0, 0), (2, 0), (0, 2), (2, 2), (1, 0)}
|
||
|
>>> find_repeat([['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']], 1, 1, 3)
|
||
|
set()
|
||
|
"""
|
||
|
|
||
|
column = size - 1 - column
|
||
|
visited = set()
|
||
|
repeated = set()
|
||
|
|
||
|
if (color := matrix_g[column][row]) != "-":
|
||
|
|
||
|
def dfs(row_n: int, column_n: int) -> None:
|
||
|
if row_n < 0 or row_n >= size or column_n < 0 or column_n >= size:
|
||
|
return
|
||
|
if (row_n, column_n) in visited:
|
||
|
return
|
||
|
visited.add((row_n, column_n))
|
||
|
if matrix_g[row_n][column_n] == color:
|
||
|
repeated.add((row_n, column_n))
|
||
|
dfs(row_n - 1, column_n)
|
||
|
dfs(row_n + 1, column_n)
|
||
|
dfs(row_n, column_n - 1)
|
||
|
dfs(row_n, column_n + 1)
|
||
|
|
||
|
dfs(column, row)
|
||
|
|
||
|
return repeated
|
||
|
|
||
|
|
||
|
def increment_score(count: int) -> int:
|
||
|
"""
|
||
|
Calculates the score for a move based on the number of elements removed.
|
||
|
|
||
|
>>> increment_score(3)
|
||
|
6
|
||
|
>>> increment_score(0)
|
||
|
0
|
||
|
"""
|
||
|
return int(count * (count + 1) / 2)
|
||
|
|
||
|
|
||
|
def move_x(matrix_g: list[list[str]], column: int, size: int) -> list[list[str]]:
|
||
|
"""
|
||
|
Simulates gravity in a specific column.
|
||
|
|
||
|
>>> move_x([['-', 'A'], ['-', '-'], ['-', 'C']], 1, 2)
|
||
|
[['-', '-'], ['-', 'A'], ['-', 'C']]
|
||
|
"""
|
||
|
|
||
|
new_list = []
|
||
|
|
||
|
for row in range(size):
|
||
|
if matrix_g[row][column] != "-":
|
||
|
new_list.append(matrix_g[row][column])
|
||
|
else:
|
||
|
new_list.insert(0, matrix_g[row][column])
|
||
|
for row in range(size):
|
||
|
matrix_g[row][column] = new_list[row]
|
||
|
return matrix_g
|
||
|
|
||
|
|
||
|
def move_y(matrix_g: list[list[str]], size: int) -> list[list[str]]:
|
||
|
"""
|
||
|
Shifts all columns leftward when an entire column becomes empty.
|
||
|
|
||
|
>>> move_y([['-', 'A'], ['-', '-'], ['-', 'C']], 2)
|
||
|
[['A', '-'], ['-', '-'], ['-', 'C']]
|
||
|
"""
|
||
|
|
||
|
empty_columns = []
|
||
|
|
||
|
for column in range(size - 1, -1, -1):
|
||
|
if all(matrix_g[row][column] == "-" for row in range(size)):
|
||
|
empty_columns.append(column)
|
||
|
|
||
|
for column in empty_columns:
|
||
|
for col in range(column + 1, size):
|
||
|
for row in range(size):
|
||
|
matrix_g[row][col - 1] = matrix_g[row][col]
|
||
|
for row in range(size):
|
||
|
matrix_g[row][-1] = "-"
|
||
|
|
||
|
return matrix_g
|
||
|
|
||
|
|
||
|
def play(
|
||
|
matrix_g: list[list[str]], pos_x: int, pos_y: int, size: int
|
||
|
) -> tuple[list[list[str]], int]:
|
||
|
"""
|
||
|
Processes a single move, updating the matrix and calculating the score.
|
||
|
|
||
|
>>> play([['R', 'G'], ['R', 'G']], 0, 0, 2)
|
||
|
([['G', '-'], ['G', '-']], 3)
|
||
|
"""
|
||
|
|
||
|
same_colors = find_repeat(matrix_g, pos_x, pos_y, size)
|
||
|
|
||
|
if len(same_colors) != 0:
|
||
|
for pos in same_colors:
|
||
|
matrix_g[pos[0]][pos[1]] = "-"
|
||
|
for column in range(size):
|
||
|
matrix_g = move_x(matrix_g, column, size)
|
||
|
|
||
|
matrix_g = move_y(matrix_g, size)
|
||
|
|
||
|
return (matrix_g, increment_score(len(same_colors)))
|
||
|
|
||
|
|
||
|
def process_game(size: int, matrix: list[str], moves: list[tuple[int, int]]) -> int:
|
||
|
"""Processes the game logic for the given matrix and moves.
|
||
|
|
||
|
Args:
|
||
|
size (int): Size of the game board.
|
||
|
matrix (List[str]): Initial game matrix.
|
||
|
moves (List[Tuple[int, int]]): List of moves as (x, y) coordinates.
|
||
|
|
||
|
Returns:
|
||
|
int: The total score obtained.
|
||
|
>>> process_game(3, ['aaa', 'bbb', 'ccc'], [(0, 0)])
|
||
|
6
|
||
|
"""
|
||
|
|
||
|
game_matrix = [list(row) for row in matrix]
|
||
|
total_score = 0
|
||
|
|
||
|
for move in moves:
|
||
|
pos_x, pos_y = move
|
||
|
game_matrix, score = play(game_matrix, pos_x, pos_y, size)
|
||
|
total_score += score
|
||
|
|
||
|
return total_score
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
import doctest
|
||
|
|
||
|
doctest.testmod(verbose=True)
|
||
|
try:
|
||
|
size = int(input("Enter the size of the matrix: "))
|
||
|
validate_matrix_size(size)
|
||
|
print(f"Enter the {size} rows of the matrix:")
|
||
|
matrix = [input(f"Row {i+1}: ") for i in range(size)]
|
||
|
validate_matrix_content(matrix, size)
|
||
|
moves_input = input("Enter the moves (e.g., '0 0, 1 1'): ")
|
||
|
moves = parse_moves(moves_input)
|
||
|
validate_moves(moves, size)
|
||
|
score = process_game(size, matrix, moves)
|
||
|
print(f"Total score: {score}")
|
||
|
except ValueError as e:
|
||
|
print(f"{e}")
|