Решение на Шахматни фенове от Антоан Ивайлов

Обратно към всички решения

Към профила на Антоан Ивайлов

Резултати

  • 10 точки от тестове
  • 0 бонус точки
  • 10 точки общо
  • 17 успешни тест(а)
  • 0 неуспешни тест(а)

Код

class ChessException(Exception):
pass
class ChessScore:
def __init__(self, figures=None):
self._figures = figures
self._score = self._calculate_board_val() # Calculate the value once, since we won't make changes to list
def _calculate_piece_value(self, piece):
pieces_values = {'r': 5, 'n': 3, 'b': 3, 'q': 9, 'k': 4, 'p': 1}
return pieces_values.get(piece, 0)
def _calculate_board_val(self):
board_score = 0
if self._figures is None:
return 0
for piece in self._figures:
board_score += self._calculate_piece_value(piece)
return board_score
def __int__(self):
return self._score
def __lt__(self, other_score_obj):
return int(self) < int(other_score_obj)
def __le__(self, other_score_obj):
return int(self) <= int(other_score_obj)
def __eq__(self, other_score_obj):
return int(self) == int(other_score_obj)
# def __ne__(self, other_score_obj): # Python 3 does fill automatically __ne__, as long as __eq__ is defined
# Same goes for __gt__ and __ge__ as long as __lt__ and __le__ are defined
def __add__(self, other_score_obj):
return int(self) + int(other_score_obj)
def __sub__(self, other_score_obj):
return int(self) - int(other_score_obj)
class ChessPosition:
def __init__(self, fen_chess_placement):
self.fen_chess_placement = fen_chess_placement
self.board = self._built_board(fen_chess_placement)
self._validate_board()
def _built_row(self, raw_row):
playable_row = []
for curr_piece in raw_row:
if curr_piece.isdigit():
for _ in range(int(curr_piece)):
playable_row.append(' ')
else:
playable_row.append(curr_piece)
return playable_row
def _built_board(self, fen_chess_placement):
raw_rows = fen_chess_placement.split('/')
playable_rows = []
for row in raw_rows:
playable_rows.append(self._built_row(row)) # We append in order to have a matrix
return playable_rows
def _check_pawns(self):
for problematic_row_index in (0, 7):
for figure_contender in self.board[problematic_row_index]:
if figure_contender in ('p', 'P'):
raise ChessException('pawns')
@staticmethod
def _is_king(self, figure_contender):
return figure_contender in ('k', 'K')
@classmethod
def _process_doublet_kings(cls, kings_collection, figure_contender):
if figure_contender in kings_collection:
raise ChessException('kings')
else:
kings_collection.append(figure_contender)
def _validate_kings_positions(self, kings_coordinates):
king_a_coords, king_b_coords = kings_coordinates
if abs(king_a_coords[0] - king_b_coords[0]) <= 1 and abs(king_a_coords[1] - king_b_coords[1]) <= 1:
raise ChessException('kings')
def _validate_kings(self):
kings_collection = []
kings_coordinates = []
curr_row_index = 0
while curr_row_index < len(self.board):
curr_column_index = 0
while curr_column_index < len(self.board[curr_row_index]):
piece_contender = self.board[curr_row_index][curr_column_index]
if self._is_king(self, piece_contender):
self._process_doublet_kings(kings_collection, piece_contender) # Check if the king is already there
kings_coordinates.append((curr_row_index, curr_column_index))
curr_column_index += 1
curr_row_index += 1
if set(kings_collection) != {'k', 'K'}: # Check if the kings are exactly 2
raise ChessException('kings')
self._validate_kings_positions(kings_coordinates)
def _validate_board(self):
self._validate_kings()
self._check_pawns()
def _get_white_pieces(self):
white_pieces = []
for curr_piece in self.fen_chess_placement:
if curr_piece.isupper():
white_pieces.append(curr_piece.lower()) # For ChessScore to work we must have only lower chars
return white_pieces
def get_white_score(self):
return ChessScore(self._get_white_pieces())
def _get_black_pieces(self):
black_pieces = []
for curr_piece in self.fen_chess_placement:
if curr_piece in ('r', 'n', 'b', 'q', 'k', 'p'):
black_pieces.append(curr_piece)
return black_pieces
def get_black_score(self):
return ChessScore(self._get_black_pieces())
def white_is_winning(self):
return self.get_white_score() > self.get_black_score()
def black_is_winning(self):
return self.get_white_score() < self.get_black_score()
def is_equal(self):
return self.get_white_score() == self.get_black_score()
def __str__(self):
return self.fen_chess_placement
def __len__(self):
return len(self._get_black_pieces()) + len(self._get_white_pieces())
def _convert_column_coord_to_playable(self, raw_column):
raw_column.capitalize() # Another alternative to dict may be to convert to ascii and subtract 'A'
letter_to_digit_chart = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
return letter_to_digit_chart[raw_column]
def __getitem__(self, piece_coords):
input_column, input_row = piece_coords
input_column = self._convert_column_coord_to_playable(input_column)
input_row = 8 - int(input_row) # Start subtracting from 8 in order to compensate for counting offset
searched_piece = self.board[input_row][input_column] # For better code readability
return searched_piece if searched_piece != ' ' else None

Лог от изпълнението

.................
----------------------------------------------------------------------
Ran 17 tests in 0.169s

OK

История (2 версии и 9 коментара)

Антоан обнови решението на 28.11.2022 15:39 (преди над 1 година)

+class ChessException(Exception):
+ def __init__(self, message):
+ self.message = message
+
+
+class ChessScore:
+ def __init__(self, figures=None):
+ self._figures = figures
+ self._score = self._calculate_board_val() # Calculate the value once, since we won't make changes to list
+
+ def _calculate_piece_value(self, piece):
+ pieces_values = {'r': 5, 'n': 3, 'b': 3, 'q': 9, 'k': 4, 'p': 1}
+ return 0 if piece not in pieces_values else pieces_values[piece]
+
+ def _calculate_board_val(self):
+ board_score = 0
+ if self._figures is None:
+ return 0
+ for piece in self._figures:
+ board_score += self._calculate_piece_value(piece)
+ return board_score
+
+ def __int__(self):
+ return self._score
+
+ def __lt__(self, other_score_obj):
+ return int(self) < int(other_score_obj)
+
+ def __le__(self, other_score_obj):
+ return int(self) <= int(other_score_obj)
+
+ def __eq__(self, other_score_obj):
+ return int(self) == int(other_score_obj)
+
+ def __ne__(self, other_score_obj): # Python 3 does it automatically btw, as long as __eq__ is defined
+ return not self == other_score_obj
+
+ def __gt__(self, other_score_obj):
+ return int(self) > int(other_score_obj)
+
+ def __add__(self, other_score_obj):
+ return int(self) + int(other_score_obj)
+
+ def __sub__(self, other_score_obj):
+ return int(self) - int(other_score_obj)
+
+
+class ChessPosition:
+ def __init__(self, fen_chess_placement):
+ self.fen_chess_placement = fen_chess_placement
+ self.board = self._built_board(fen_chess_placement)
+ self._validate_board()
+
+ def _built_row(self, raw_row): # OK
+ playable_row = []
+ for curr_piece in raw_row:
+ if curr_piece.isdigit():
+ curr_space_index = 0
+ while curr_space_index < int(curr_piece):
+ playable_row.append(' ')
+ curr_space_index += 1
+ else:
+ playable_row.append(curr_piece)
+ return playable_row
+
+ def _built_board(self, fen_chess_placement): # OK
+ raw_rows = fen_chess_placement.split('/')
+ playable_rows = []
+ curr_row_index = 0
+ while curr_row_index < len(raw_rows):
+ playable_rows.append(self._built_row(raw_rows[curr_row_index])) # We append in order to have a matrix
+ curr_row_index += 1
+ return playable_rows
+
+ def _check_pawns(self):
+ for problematic_row_index in (0, 7):
+ for figure_contender in self.board[problematic_row_index]:
+ if figure_contender in ('p', 'P'):
+ raise ChessException('pawns')
+
+ @staticmethod
+ def _is_king(self, figure_contender):
+ return figure_contender in ('k', 'K')
+
+ @classmethod
+ def _process_doublet_kings(cls, kings_collection, figure_contender):
+ if figure_contender in kings_collection:
+ raise ChessException('kings')
+ else:
+ kings_collection.append(figure_contender)
+
+ def _touches_king_left(self, king_row, king_column):
+ if king_column == 0:
+ return False
+ else:
+ return self._is_king(self, self.board[king_row][king_column - 1])
+
+ def _touches_king_top_left(self, king_row, king_column):
+ if king_row == 0 or king_column == 0:
+ return False
+ else:
+ return self._is_king(self, self.board[king_row - 1][king_column - 1])
+
+ def _touches_king_top_mid(self, king_row, king_column):
+ if king_row == 0:
+ return False
+ else:
+ return self._is_king(self, self.board[king_row - 1][king_column])
+
+ def _touches_king_top_right(self, king_row, king_column):
+ if king_row == 0 or king_column == 7:
+ return False
+ else:
+ return self._is_king(self, self.board[king_row - 1][king_column + 1])
+
+ def _process_invalid_kings_position(self, king_row, king_column):

Бих взел точните координате на двата царя (след като вече съм валидирал, че са точно 2), изчислил разстоянието между тях и сравнил дали е по-малко от едно. Ще спестиш доста логика.

+ # Since we traverse top-left to bottom right, we should check only top part(top-left, top, top-right) and left part
+
+ if (self._touches_king_left(king_row, king_column) or self._touches_king_top_left(king_row, king_column) or
+ self._touches_king_top_mid(king_row, king_column) or self._touches_king_top_right(king_row,
+ king_column)):
+ raise ChessException('kings')
+
+ def _validate_kings(self):
+ kings_collection = []
+ curr_row_index = 0
+ while curr_row_index < len(self.board):
+ curr_column_index = 0
+ while curr_column_index < len(self.board[curr_row_index]):
+ piece_contender = self.board[curr_row_index][curr_column_index]
+ if self._is_king(self, piece_contender):
+ self._process_doublet_kings(kings_collection, piece_contender)
+ self._process_invalid_kings_position(curr_row_index, curr_column_index)
+ curr_column_index += 1
+ curr_row_index += 1
+ if set(kings_collection) != {'k', 'K'}: # Check if the kings are exactly 2
+ raise ChessException('kings')
+
+ def _validate_board(self):
+ self._validate_kings()
+ self._check_pawns()
+
+ def _get_white_pieces(self):
+ white_pieces = []
+ for curr_piece in self.fen_chess_placement:
+ if curr_piece in ('R', 'N', 'B', 'Q', 'K', 'P'):

Не бях сигурен тук, дали случайно няма да изгърми за '/' или някой невалиден символ за класа ChessScores(цифра, ' ', друг невалиден ASCII код). Но щом предлагаш навярно тестовете няма да са толкова гадни. *имам flashbacks от празния инпут на домашното с телефона и двете поредни невалдни цифри хД

+ white_pieces.append(curr_piece.lower()) # For ChessScore to work we must have only lower chars
+ return white_pieces
+
+ def get_white_score(self):
+ return ChessScore(self._get_white_pieces())
+
+ def _get_black_pieces(self):
+ black_pieces = []
+ for curr_piece in self.fen_chess_placement:
+ if curr_piece in ('r', 'n', 'b', 'q', 'k', 'p'):
+ black_pieces.append(curr_piece)
+ return black_pieces
+
+ def get_black_score(self):
+ return ChessScore(self._get_black_pieces())
+
+ def white_is_winning(self):
+ return self.get_white_score() > self.get_black_score()
+
+ def black_is_winning(self):
+ return self.get_white_score() < self.get_black_score()
+
+ def is_equal(self):
+ return self.get_white_score() == self.get_black_score()
+
+ def __str__(self):
+ return self.fen_chess_placement
+
+ def __len__(self):
+ return len(self._get_black_pieces()) + len(self._get_white_pieces())
+
+ def _convert_column_coord_to_playable(self, raw_column):
+ raw_column.capitalize() # another alternative to dict may be to convert to ascii and subtract 'A'
+ letter_to_digit_chart = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
+ return letter_to_digit_chart[raw_column]
+
+ def _convert_row_coord_to_playable(self, raw_row): # raw_row spans from [8 to 1]

Според мен логиката в този метод не заслужава собствена функция. Има и други неща по-горе, за които не бях сигурен, но това е прекалено лесно и се използва на само едно място (дори да приемем, че ще скейваш, пак не мисля, че ще ти трябва). Не е никакъв проблем, понеже видях няколко подобни - споделям. Много е хубаво кодът да е атомарен и всеки метод да прави точно едно нещо, но ако това нещо е един ред прост експрешън, без някаква силна причина зад него, по-скоро затормозяваш четенето на кода. Мое мнение - бъди свободен да не се съгласиш :)

+ return 8 - int(raw_row) # Start subtracting from 8 in order to compensate for counting offset
+
+ def __getitem__(self, piece_coords):
+ input_column, input_row = piece_coords
+ input_column = self._convert_column_coord_to_playable(input_column)
+ input_row = self._convert_row_coord_to_playable(input_row)
+ searched_piece = self.board[input_row][input_column] # For better code readability
+ return searched_piece if searched_piece != ' ' else None

Антоан обнови решението на 28.11.2022 20:34 (преди над 1 година)

class ChessException(Exception):
- def __init__(self, message):
- self.message = message
+ pass
class ChessScore:
def __init__(self, figures=None):
self._figures = figures
self._score = self._calculate_board_val() # Calculate the value once, since we won't make changes to list
def _calculate_piece_value(self, piece):
pieces_values = {'r': 5, 'n': 3, 'b': 3, 'q': 9, 'k': 4, 'p': 1}
- return 0 if piece not in pieces_values else pieces_values[piece]
+ return pieces_values.get(piece, 0)
def _calculate_board_val(self):
board_score = 0
if self._figures is None:
return 0
for piece in self._figures:
board_score += self._calculate_piece_value(piece)
return board_score
def __int__(self):
return self._score
def __lt__(self, other_score_obj):
return int(self) < int(other_score_obj)
def __le__(self, other_score_obj):
return int(self) <= int(other_score_obj)
def __eq__(self, other_score_obj):
return int(self) == int(other_score_obj)
- def __ne__(self, other_score_obj): # Python 3 does it automatically btw, as long as __eq__ is defined
- return not self == other_score_obj
+ # def __ne__(self, other_score_obj): # Python 3 does fill automatically __ne__, as long as __eq__ is defined
+ # Same goes for __gt__ and __ge__ as long as __lt__ and __le__ are defined
- def __gt__(self, other_score_obj):
- return int(self) > int(other_score_obj)
-
def __add__(self, other_score_obj):
return int(self) + int(other_score_obj)
def __sub__(self, other_score_obj):
return int(self) - int(other_score_obj)
class ChessPosition:
def __init__(self, fen_chess_placement):
self.fen_chess_placement = fen_chess_placement
self.board = self._built_board(fen_chess_placement)
self._validate_board()
- def _built_row(self, raw_row): # OK
+ def _built_row(self, raw_row):
playable_row = []
for curr_piece in raw_row:
if curr_piece.isdigit():
- curr_space_index = 0
- while curr_space_index < int(curr_piece):
+ for _ in range(int(curr_piece)):
playable_row.append(' ')
- curr_space_index += 1
else:
playable_row.append(curr_piece)
return playable_row
- def _built_board(self, fen_chess_placement): # OK
+ def _built_board(self, fen_chess_placement):
raw_rows = fen_chess_placement.split('/')
playable_rows = []
- curr_row_index = 0
- while curr_row_index < len(raw_rows):
- playable_rows.append(self._built_row(raw_rows[curr_row_index])) # We append in order to have a matrix
- curr_row_index += 1
+ for row in raw_rows:
+ playable_rows.append(self._built_row(row)) # We append in order to have a matrix
return playable_rows
def _check_pawns(self):
for problematic_row_index in (0, 7):
for figure_contender in self.board[problematic_row_index]:
if figure_contender in ('p', 'P'):
raise ChessException('pawns')
@staticmethod
def _is_king(self, figure_contender):
return figure_contender in ('k', 'K')
@classmethod
def _process_doublet_kings(cls, kings_collection, figure_contender):
if figure_contender in kings_collection:
raise ChessException('kings')
else:
kings_collection.append(figure_contender)
- def _touches_king_left(self, king_row, king_column):
- if king_column == 0:
- return False
- else:
- return self._is_king(self, self.board[king_row][king_column - 1])
-
- def _touches_king_top_left(self, king_row, king_column):
- if king_row == 0 or king_column == 0:
- return False
- else:
- return self._is_king(self, self.board[king_row - 1][king_column - 1])
-
- def _touches_king_top_mid(self, king_row, king_column):
- if king_row == 0:
- return False
- else:
- return self._is_king(self, self.board[king_row - 1][king_column])
-
- def _touches_king_top_right(self, king_row, king_column):
- if king_row == 0 or king_column == 7:
- return False
- else:
- return self._is_king(self, self.board[king_row - 1][king_column + 1])
-
- def _process_invalid_kings_position(self, king_row, king_column):
- # Since we traverse top-left to bottom right, we should check only top part(top-left, top, top-right) and left part
-
- if (self._touches_king_left(king_row, king_column) or self._touches_king_top_left(king_row, king_column) or
- self._touches_king_top_mid(king_row, king_column) or self._touches_king_top_right(king_row,
- king_column)):
+ def _validate_kings_positions(self, kings_coordinates):
+ king_a_coords, king_b_coords = kings_coordinates
+ if abs(king_a_coords[0] - king_b_coords[0]) <= 1 and abs(king_a_coords[1] - king_b_coords[1]) <= 1:
raise ChessException('kings')
def _validate_kings(self):
kings_collection = []
+ kings_coordinates = []
curr_row_index = 0
while curr_row_index < len(self.board):
curr_column_index = 0
while curr_column_index < len(self.board[curr_row_index]):
piece_contender = self.board[curr_row_index][curr_column_index]
if self._is_king(self, piece_contender):
- self._process_doublet_kings(kings_collection, piece_contender)
- self._process_invalid_kings_position(curr_row_index, curr_column_index)
+ self._process_doublet_kings(kings_collection, piece_contender) # Check if the king is already there
+ kings_coordinates.append((curr_row_index, curr_column_index))
curr_column_index += 1
curr_row_index += 1
if set(kings_collection) != {'k', 'K'}: # Check if the kings are exactly 2
raise ChessException('kings')
+ self._validate_kings_positions(kings_coordinates)
def _validate_board(self):
self._validate_kings()
self._check_pawns()
def _get_white_pieces(self):
white_pieces = []
for curr_piece in self.fen_chess_placement:
- if curr_piece in ('R', 'N', 'B', 'Q', 'K', 'P'):
+ if curr_piece.isupper():
white_pieces.append(curr_piece.lower()) # For ChessScore to work we must have only lower chars
return white_pieces
def get_white_score(self):
return ChessScore(self._get_white_pieces())
def _get_black_pieces(self):
black_pieces = []
for curr_piece in self.fen_chess_placement:
if curr_piece in ('r', 'n', 'b', 'q', 'k', 'p'):
black_pieces.append(curr_piece)
return black_pieces
def get_black_score(self):
return ChessScore(self._get_black_pieces())
def white_is_winning(self):
return self.get_white_score() > self.get_black_score()
def black_is_winning(self):
return self.get_white_score() < self.get_black_score()
def is_equal(self):
return self.get_white_score() == self.get_black_score()
def __str__(self):
return self.fen_chess_placement
def __len__(self):
return len(self._get_black_pieces()) + len(self._get_white_pieces())
def _convert_column_coord_to_playable(self, raw_column):
- raw_column.capitalize() # another alternative to dict may be to convert to ascii and subtract 'A'
+ raw_column.capitalize() # Another alternative to dict may be to convert to ascii and subtract 'A'
letter_to_digit_chart = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
return letter_to_digit_chart[raw_column]
- def _convert_row_coord_to_playable(self, raw_row): # raw_row spans from [8 to 1]
- return 8 - int(raw_row) # Start subtracting from 8 in order to compensate for counting offset
-
def __getitem__(self, piece_coords):
input_column, input_row = piece_coords
input_column = self._convert_column_coord_to_playable(input_column)
- input_row = self._convert_row_coord_to_playable(input_row)
+ input_row = 8 - int(input_row) # Start subtracting from 8 in order to compensate for counting offset
searched_piece = self.board[input_row][input_column] # For better code readability
return searched_piece if searched_piece != ' ' else None