diff --git a/abc.py b/abc.py
new file mode 100644
index 0000000..eade1a7
--- /dev/null
+++ b/abc.py
@@ -0,0 +1,28 @@
+def split_and_join(line):
+ full = []
+ current = ''
+ line_len = len(line)
+ for c in range(line_len):
+ char = line[c]
+ if char == ' ':
+ full.append(current)
+ current = ''
+ else:
+ current = current + char
+
+ if c == line_len - 1:
+ full.append(str(current))
+
+ full_final = ''
+ full_len = len(full)
+ for v in range(full_len):
+ val = full[v]
+ full_final += val + ('' if v == full_len-1 else '-')
+
+ return full_final
+
+if __name__ == '__main__':
+ line = input()
+ result = split_and_join(line)
+ print(result)
+
diff --git a/book_stuff/student.py b/book_stuff/student.py
new file mode 100644
index 0000000..f6c32d6
--- /dev/null
+++ b/book_stuff/student.py
@@ -0,0 +1,43 @@
+class Student:
+ def __init__(self, name: str, gNumber: int, gpa: float):
+ self.set_name(name)
+ self.set_gNumber(gNumber)
+ self.set_gpa(gpa)
+
+ # --- Name ---
+ def get_name(self) -> str:
+ return self._name
+
+ def set_name(self, value: str) -> None:
+ if not isinstance(value, str):
+ raise TypeError("Name must be a string")
+ if not value.isalpha():
+ raise ValueError("Name must contain only alphabetic characters")
+ if not (1 <= len(value) <= 255):
+ raise ValueError("Name must be between 1 and 255 characters")
+ self._name = value
+
+ # --- gNumber ---
+ def get_gNumber(self) -> int:
+ return self._gNumber
+
+ def set_gNumber(self, value: int) -> None:
+ if not isinstance(value, int):
+ raise TypeError("gNumber must be an integer")
+ if not (100000 <= value <= 999999):
+ raise ValueError("gNumber must be a 6-digit number between 100000 and 999999")
+ self._gNumber = value
+
+ # --- GPA ---
+ def get_gpa(self) -> float:
+ return self._gpa
+
+ def set_gpa(self, value: float) -> None:
+ if not isinstance(value, (int, float)):
+ raise TypeError("GPA must be a number")
+ if not (0.0 <= value <= 6.0):
+ raise ValueError("GPA must be between 0.0 and 6.0")
+ self._gpa = float(value)
+
+ def __str__(self) -> str:
+ return f"Student(Name: {self._name}, gNumber: {self._gNumber}, GPA: {self._gpa:.2f})"
\ No newline at end of file
diff --git a/book_stuff/test_student.py b/book_stuff/test_student.py
new file mode 100644
index 0000000..cd310b1
--- /dev/null
+++ b/book_stuff/test_student.py
@@ -0,0 +1,90 @@
+import pytest
+from student import Student # assumes Student class is in student.py
+
+
+# ---------- FIXTURES ----------
+@pytest.fixture
+def valid_student():
+ """Fixture for a valid student object"""
+ return Student("Alice", 123456, 4.0)
+
+def test_create_valid_student(valid_student):
+ assert valid_student.get_name() == "Alice"
+ assert valid_student.get_gNumber() == 123456
+ assert valid_student.get_gpa() == 4.0
+
+
+def test_update_name(valid_student):
+ valid_student.set_name("Bob")
+ assert valid_student.get_name() == "Bob"
+
+
+def test_update_gNumber(valid_student):
+ valid_student.set_gNumber(654321)
+ assert valid_student.get_gNumber() == 654321
+
+
+def test_update_gpa(valid_student):
+ valid_student.set_gpa(5.5)
+ assert valid_student.get_gpa() == 5.5
+
+
+# ---------- NAME VALIDATION ----------
+@pytest.mark.parametrize("invalid_name", [
+ "", # empty string
+ "A" * 256, # too long
+ "Alice123", # contains digits
+ "Alice!", # contains special character
+ 123, # not a string
+])
+def test_invalid_name_raises(invalid_name):
+ with pytest.raises((ValueError, TypeError)):
+ Student(invalid_name, 123456, 3.0)
+
+
+# ---------- gNumber VALIDATION ----------
+@pytest.mark.parametrize("invalid_gNumber", [
+ 99999, # too small
+ 1000000, # too large
+ "123456", # not an int
+ 12.34, # float
+])
+def test_invalid_gNumber_raises(invalid_gNumber):
+ with pytest.raises((ValueError, TypeError)):
+ Student("Charlie", invalid_gNumber, 3.5)
+
+
+def test_boundary_gNumber_valid():
+ s1 = Student("David", 100000, 2.0)
+ s2 = Student("Eve", 999999, 3.0)
+ assert s1.get_gNumber() == 100000
+ assert s2.get_gNumber() == 999999
+
+
+# ---------- GPA VALIDATION ----------
+@pytest.mark.parametrize("invalid_gpa", [
+ -0.1, # below range
+ 6.1, # above range
+ "4.0", # string instead of number
+ None, # NoneType
+])
+def test_invalid_gpa_raises(invalid_gpa):
+ with pytest.raises((ValueError, TypeError)):
+ Student("Frank", 222222, invalid_gpa)
+
+
+def test_boundary_gpa_valid():
+ s1 = Student("Grace", 333333, 0.0)
+ s2 = Student("Heidi", 444444, 6.0)
+ assert s1.get_gpa() == 0.0
+ assert s2.get_gpa() == 6.0
+
+
+# ---------- STRING REPRESENTATION ----------
+def test_str_representation(valid_student):
+ s = str(valid_student)
+ assert "Alice" in s
+ assert "123456" in s
+ assert "4.00" in s
+
+
diff --git a/chess/chess_gui_small_view.py b/chess/chess_gui_small_view.py
new file mode 100644
index 0000000..c43c08e
--- /dev/null
+++ b/chess/chess_gui_small_view.py
@@ -0,0 +1,164 @@
+import copy
+from enum import Enum
+import pygame as pg
+import pygame_gui as gui
+from chess_model import ChessModel, MoveValidity, UndoException
+from move import Move
+from player import Player
+
+IMAGE_SIZE = 52 #small format - images 52 X 52
+
+
+class SpriteType(Enum):
+ King = 0
+ Queen = 1
+ Bishop = 2
+ Knight = 3
+ Rook = 4
+ Pawn = 5
+
+class SpriteColor(Enum):
+ WHITE = 0
+ BLACK = 1
+
+class GUI:
+ first = True
+ def __init__(self) -> None:
+ pg.init()
+ self.__model = ChessModel()
+ self._screen = pg.display.set_mode((800, 600))
+ pg.display.set_caption("Laker Chess")
+ self._ui_manager = gui.UIManager((800, 600))
+ self._side_box = gui.elements.UITextBox('Laker Chess
White moves first.
',
+ relative_rect=pg.Rect((500, 100), (400, 500)),
+ manager=self._ui_manager)
+ self._undo_button = gui.elements.UIButton(relative_rect = pg.Rect((700, 50), (100, 50)),
+ text='Undo',
+ manager=self._ui_manager)
+ self._restart_button = gui.elements.UIButton(relative_rect = pg.Rect((600, 50), (100, 50)),
+ text='Reset',
+ manager=self._ui_manager)
+ self._piece_selected = False
+ self._first_selected = (0, 0)
+ self._second_selected = (0, 0)
+
+ @classmethod
+ def load_images(cls):
+ def load_image(color, ptype):
+ SS = pg.image.load('./images/pieces.png')
+ a = 105
+ surf = pg.Surface((a,a), pg.SRCALPHA)
+ surf.blit(SS, (0, 0), pg.rect.Rect(a*ptype.value, color.value*a, a, a))
+ surf_scaled = pg.transform.scale(surf, (IMAGE_SIZE, IMAGE_SIZE))
+ return surf_scaled
+ cls.white_sprites = {}
+ cls.black_sprites = {}
+ for st in SpriteType:
+ cls.white_sprites[st.name] = load_image(SpriteColor.WHITE, st)
+ cls.black_sprites[st.name] = load_image(SpriteColor.BLACK, st)
+
+ def run_game(self) -> None:
+ running = True
+ time_delta = 0
+ clock = pg.time.Clock()
+ while running:
+ for event in pg.event.get():
+ if event.type == pg.QUIT:
+ running = False
+ if event.type == pg.MOUSEBUTTONDOWN:
+ x, y = pg.mouse.get_pos()
+ y, x = self.__get_coords__(y, x)
+ piece = self.__model.piece_at(y, x)
+ if not self._piece_selected and piece:
+ if piece.player != self.__model.current_player:
+ msg = 'Not your turn!'
+ self._side_box.append_html_text(msg + '
')
+ else:
+ self._piece_selected = True
+ self._first_selected = y, x
+ self._piece_selected = piece
+ elif self._piece_selected:
+ mv = Move(self._first_selected[0], self._first_selected[1], y, x)
+ if self.__model.is_valid_move(mv):
+ target = self.__model.piece_at(y, x)
+ self.__model.move(mv)
+ if target is not None:
+ msg = f'Moved {self._piece_selected} and captured {target}'
+ else:
+ msg = f'Moved {self._piece_selected}'
+ self._side_box.append_html_text(msg + '
')
+
+ else:
+ self._side_box.append_html_text(f'{self.__model.messageCode}
')
+ incheck = self.__model.in_check(self.__model.current_player)
+ complete = self.__model.is_complete()
+
+ if incheck:
+ player_color = self.__model.current_player.name
+ if complete:
+ self._side_box.append_html_text(f'{player_color} is in CHECKMATE!
GAME OVER!')
+ else:
+ self._side_box.append_html_text(f'{player_color} is in CHECK!
')
+
+ self._piece_selected = False
+ else:
+ self._piece_selected = False
+ if event.type == gui.UI_BUTTON_PRESSED:
+ if event.ui_element == self._restart_button:
+ self.__model = ChessModel()
+ self._side_box.set_text("Restarting game...
")
+ if event.ui_element == self._undo_button:
+ try:
+ self.__model.undo()
+ self._side_box.append_html_text('Undoing move.
')
+ except UndoException as e:
+ self._side_box.append_html_text(f'{e}
')
+ self._ui_manager.process_events(event)
+
+ self._screen.fill((255, 255, 255))
+ self.__draw_board__()
+ self._ui_manager.draw_ui(self._screen)
+ self._ui_manager.update(time_delta)
+
+ pg.display.flip()
+ time_delta = clock.tick(30) / 1000.0
+
+ def __get_coords__(self, y, x):
+ grid_x = x // IMAGE_SIZE
+ grid_y = y // IMAGE_SIZE
+ return grid_y, grid_x
+
+ def __draw_board__(self) -> None:
+ count = 0
+ color = (255, 255, 255)
+ for x in range(0, 8):
+ for y in range(0, 8):
+ if count % 2 == 0:
+ color = (255, 255, 255)
+ else:
+ color = (127, 127, 127)
+ count = count + 1
+ pg.draw.rect(self._screen, color, pg.rect.Rect(x * IMAGE_SIZE, y * IMAGE_SIZE, IMAGE_SIZE, IMAGE_SIZE))
+ if self._piece_selected and (y, x) == self._first_selected:
+ pg.draw.rect(self._screen, (255, 0, 0), pg.rect.Rect(x * IMAGE_SIZE, y * IMAGE_SIZE, IMAGE_SIZE, IMAGE_SIZE), 2)
+ draw_piece = self.__model.piece_at(y, x)
+ if draw_piece is not None:
+ if draw_piece.player == Player.BLACK:
+ d = GUI.black_sprites
+ else:
+ d = GUI.white_sprites
+ self._screen.blit(copy.deepcopy(d[draw_piece.type()]), (x * IMAGE_SIZE, y * IMAGE_SIZE))
+ count = count + 1
+ pg.draw.line(self._screen, (0, 0, 0), (0, 840), (840, 840))
+ pg.draw.line(self._screen, (0, 0, 0), (840, 840), (840, 0))
+ GUI.first = False
+
+
+def main():
+ GUI.load_images()
+ g = GUI()
+ g.run_game()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/chess/chess_model.py b/chess/chess_model.py
new file mode 100644
index 0000000..104c6b7
--- /dev/null
+++ b/chess/chess_model.py
@@ -0,0 +1,35 @@
+from enum import Enum
+from player import Player
+from move import Move
+from chess_piece import ChessPiece
+from pawn import Pawn
+from rook import Rook
+from knight import Knight
+from bishop import Bishop
+from queen import Queen
+from king import King
+from move import Move
+
+class MoveValidity(Enum):
+ Valid = 1
+ Invalid = 2
+ MovingIntoCheck = 3
+ StayingInCheck = 4
+
+ def __str__(self):
+ if self.value == 2:
+ return 'Invalid move.'
+
+ if self.value == 3:
+ return 'Invalid -- cannot move into check.'
+
+ if self.value == 4:
+ return 'Invalid -- must move out of check.'
+
+
+# TODO: create UndoException
+
+
+class ChessModel:
+ # TODO: fill in this class
+ pass
\ No newline at end of file
diff --git a/chess/chess_piece.py b/chess/chess_piece.py
new file mode 100644
index 0000000..5631238
--- /dev/null
+++ b/chess/chess_piece.py
@@ -0,0 +1,52 @@
+from player import Player
+from move import Move
+from typing import TypeVar
+from abc import ABC, abstractmethod
+
+ChessPieceT = TypeVar('ChessPieceT')
+
+# my list of custom exceptions
+class PieceOutOfBoundsError(Exception): pass
+class StartEndPositionMismatch(Exception): pass
+
+class ChessPiece:
+ def __init__(self, piece_color: Player):
+ self.player = piece_color
+
+ @property
+ def player(self):
+ return self.__player
+
+ @player.setter
+ def player(self, new_val):
+ if not isinstance(new_val, Player):
+ raise TypeError(f'new value for player is not of type Player')
+ self.__player = new_val
+
+ def __str__(self):
+ # im not making this abstract, attributes amongst each piece are the same, str repr is also dynamic for the class name
+ return f'[{self.__class__.__name__} player={self.player}]'
+
+ def is_valid_move(self, move: Move, board: list[list[ChessPieceT]]) -> bool:
+ if not isinstance(board, list):
+ raise TypeError(f'board must be a list')
+
+ for arr in board:
+ if not isinstance(arr, list):
+ raise TypeError(f'each element in the board list bust be another list')
+
+ for v in arr:
+ if not isinstance(v, ChessPiece):
+ raise TypeError(f'each element in each row of the board must be of type ChessPiece')
+
+ board_dim = len(board)
+ board_orig: ChessPiece = board[move.to_row][move.to_col]
+ board_dest: ChessPiece = board[move.from_row][move.from_col]
+ within_bounds = board_dim <= move.to_col <= board_dim and board_dim <= move.to_row <= board_dim
+ different_position = move.from_col != move.to_col and move.from_row != move.to_row
+ at_position = board_orig == self
+ is_piece_class = isinstance(board_dest, ChessPiece)
+ taking_friendly_piece = board_dest.player != self.player
+
+ print(f'within_bounds={within_bounds}, different_position={different_position}, at_position={at_position}, is_piece_class={is_piece_class}, taking_friendly_piece={taking_friendly_piece}')
+ return within_bounds and different_position and at_position and is_piece_class and taking_friendly_piece
\ No newline at end of file
diff --git a/chess/images/pieces.png b/chess/images/pieces.png
new file mode 100644
index 0000000..5e838d2
Binary files /dev/null and b/chess/images/pieces.png differ
diff --git a/chess/images/small_pieces.png b/chess/images/small_pieces.png
new file mode 100644
index 0000000..e060ad5
Binary files /dev/null and b/chess/images/small_pieces.png differ
diff --git a/chess/move.py b/chess/move.py
new file mode 100644
index 0000000..bd34a9c
--- /dev/null
+++ b/chess/move.py
@@ -0,0 +1,121 @@
+from enum import Enum
+
+class Move:
+ def __init__(self, from_row, from_col, to_row, to_col):
+ self.from_row = from_row
+ self.from_col = from_col
+ self.to_row = to_row
+ self.to_col = to_col
+
+ def __str__(self):
+ output = f'Move [from_row={self.from_row}, from_col={self.from_col}'
+ output += f', to_row={self.to_row}, to_col={self.to_col}]'
+ return output
+
+# kinda just guessing, but on prarielearn it only showed the file names included in the project and the
+# ones we need to create for the submission, i was gonna put all these in their own files, but now i'm
+# just putting it here because i dont wanna risk not being able to submit
+
+class PieceType:
+ # piece type for each piece
+ PAWN = 0
+ ROOK = 1
+ KING = 2
+ QUEEN = 3
+ KNIGHT = 4
+ BISHOP = 5
+
+# check if something is a valid move set element ( (y, x) tuple )
+def valid_move_set_element(move_set: tuple[int, int]) -> bool:
+ # check if move set is a tuple
+ if not isinstance(move_set, tuple):
+ raise TypeError(f'each move set in move sets must be a tuple ({move_set})')
+
+ # check if the length of the tuple is 2, because it needs to have a y and x
+ ms_len = len(move_set)
+ if ms_len != 2:
+ raise ValueError(f'length of move set ({move_set}) is {ms_len}, must be 2 (y and x)')
+
+ # check if each element is an int
+ for i in range(ms_len):
+ p = move_set[i]
+ if not isinstance(p, int):
+ raise TypeError(f'tuple element at index {i} ({p}) must be an int')
+
+ return True
+
+# general move set list class for each piece type
+class MoveSets:
+ def __init__(self, *move_sets: tuple[int, int]):
+ # loop over indices of move_sets, checking if each element at index i is valid, exception thrown by valid_move_set if not
+ for i in range(len(move_sets)):
+ valid_move_set_element(move_sets[i])
+
+ # set all the stuff equal
+ self.__move_sets = move_sets
+
+ @property
+ def move_sets(self):
+ return self.__move_sets
+
+ def is_valid_move(self, move: Move) -> bool:
+ # is the move valid, i dunno
+ raise NotImplementedError('u gotta implement me bruh')
+
+def valid_range(max: int) -> list[int]:
+ return [-i if max < 0 else i for i in range(1, abs(max)+1)]
+
+# class for static move sets
+class StaticMoveSet(MoveSets):
+ def is_valid_move(self, move: Move) -> bool:
+ from_to_row_diff = move.to_row - move.from_row
+ from_to_col_diff = move.to_col - move.from_col
+ for ms in self.move_sets:
+ if from_to_row_diff == ms[0] and from_to_col_diff == ms[1]:
+ return True
+
+ return False
+
+# in these lists, the move sets are dynamic, so the y and x are a range of times they can move on the x and y
+rook_valid_move_sets = [(0, 8), (8, 0), (0, -8), (-8, 0)]
+
+# class for dynamic move sets
+class DynamicMoveSet(MoveSets):
+ def is_valid_move(self, move: Move) -> bool:
+ from_to_row_diff = move.to_row - move.from_row
+ from_to_col_diff = move.to_col - move.from_col
+ # check if the to and from actually moved
+ if from_to_row_diff == 0 and from_to_col_diff == 0:
+ return False
+
+ for ms in self.move_sets:
+ possible_valid_row_moves, possible_valid_col_moves = [valid_range(mse) for mse in ms]
+
+ # check if move in row is possible, only if there are valid moves for row movement
+ if len(possible_valid_row_moves) > 0:
+ if from_to_row_diff not in possible_valid_row_moves:
+ continue
+ else:
+ # if theres no valid moves for rows, make sure theres no change in the from to row difference
+ if from_to_row_diff != 0:
+ continue
+
+ # check if move in column is possible, only if there are valid moves for column movement
+ if len(possible_valid_col_moves) > 0:
+ if from_to_col_diff not in possible_valid_col_moves:
+ continue
+ else:
+ # if theres no valid moves for columns, make sure theres no change in the from to column difference
+ if from_to_col_diff != 0:
+ continue
+
+ return True
+
+ return False
+
+# create move sets
+
+# static move sets
+pawn_move_sets = StaticMoveSet((0, 1), (0, 2))
+
+# dynamic move sets
\ No newline at end of file
diff --git a/chess/pawn.py b/chess/pawn.py
new file mode 100644
index 0000000..259aea7
--- /dev/null
+++ b/chess/pawn.py
@@ -0,0 +1,12 @@
+from chess_piece import ChessPiece
+from move import Move
+from player import Player
+from move_sets import pawn_valid_move_sets
+
+class Pawn(ChessPiece):
+ def __init__(self, piece_color: Player):
+ super().__init__(piece_color)
+
+ def is_valid_move(self, move: Move, board: list[list[ChessPiece]]) -> bool:
+ # run original check and other piece specific checks
+ orig_is_valid = super().is_valid_move(move, board)
\ No newline at end of file
diff --git a/chess/player.py b/chess/player.py
new file mode 100644
index 0000000..2de1360
--- /dev/null
+++ b/chess/player.py
@@ -0,0 +1,14 @@
+from enum import Enum
+
+class Player(Enum):
+ BLACK = 0
+ WHITE = 1
+
+ def next(self):
+ cls = self.__class__
+ members = list(cls)
+ index = members.index(self) + 1
+ if index >= len(members):
+ index = 0
+ return members[index]
+
diff --git a/chess/test.py b/chess/test.py
new file mode 100644
index 0000000..82559e0
--- /dev/null
+++ b/chess/test.py
@@ -0,0 +1,104 @@
+from chess_piece import ChessPiece
+from pytest import fixture, mark
+from player import Player
+from move import StaticMoveSet, Move, DynamicMoveSet, valid_range
+from random import randint, choice
+
+# chess piece tests
+
+@fixture
+def valid_piece():
+ return ChessPiece(Player.WHITE)
+
+def test_update_player(valid_piece: ChessPiece):
+ valid_piece.player = Player.BLACK
+ assert valid_piece.player == Player.BLACK
+
+ valid_piece.player = Player.WHITE
+ assert valid_piece.player == Player.WHITE
+
+def test_repr_str(valid_piece: ChessPiece):
+ rep = str(valid_piece)
+ assert 'player='
+
+# move set testing (kinda separate from main project)
+
+_init_val = 4
+
+# static move sets
+
+_static_move_sets = [(1, 0), (-3, 0), (0, 2), (0, -1), (4, 4), (-2, -2), (1, -3), (-3, 4)]
+
+@fixture
+def valid_static_move_set():
+ return StaticMoveSet(*_static_move_sets)
+
+# test valid
+
+def test_valid_static_moves(valid_static_move_set: StaticMoveSet):
+ for ms in _static_move_sets:
+ assert valid_static_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+ms[0], _init_val+ms[1]))
+
+# test invalid
+
+def test_invalid_static_moves(valid_static_move_set: StaticMoveSet):
+ for ms in _static_move_sets:
+ assert not valid_static_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+ms[0]+(-1 if ms[0] < 0 else 1), _init_val+ms[1]+(-1 if ms[1] < 0 else 1)))
+
+# dynamic move sets
+
+_dynamic_move_sets = [(4, 0), (-2, 0), (0, 8), (0, -6), (4, 4), (-4, -4), (2, -5), (-3, 4)]
+
+@fixture
+def valid_dynamic_move_set():
+ return DynamicMoveSet(*_dynamic_move_sets)
+
+def test_valid_dynamic_moves(valid_dynamic_move_set: DynamicMoveSet):
+ for ms in _dynamic_move_sets:
+ row = ms[0]
+ col = ms[1]
+
+ # find a valid range on numbers to select from using the row and column
+ valid_range_row = valid_range(row)
+ valid_range_col = valid_range(col)
+
+ # check if the ranges for rows and columns are empty individually, if so;
+ # set random value to 0, if not, set it to a random element from it's respective list
+ if len(valid_range_row) == 0:
+ rnd_row = 0
+ else:
+ rnd_row = choice(valid_range_row)
+
+ if len(valid_range_col) == 0:
+ rnd_col = 0
+ else:
+ rnd_col = choice(valid_range_col)
+
+ # test dat thing
+ assert valid_dynamic_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+rnd_row, _init_val+rnd_col))
+
+_RND_MIN = 10
+_RND_MAX = 20
+
+def test_invalid_dynamic_moves(valid_dynamic_move_set: DynamicMoveSet):
+ for ms in _dynamic_move_sets:
+ row = ms[0]
+ col = ms[1]
+
+ # check if the row and column ranges are equal to zero, if so;
+ # set random value to 0, if not, create random number between _rnd_min and _rnd_max
+ # then, add the random number, making it negative if the column or row in that instance > 0
+ if row == 0:
+ row_rnd_add = 0
+ else:
+ rnd = randint(_RND_MIN, _RND_MAX)
+ row_rnd_add = row + (rnd if row > 0 else -rnd)
+
+ if col == 0:
+ col_rnd_add = 0
+ else:
+ rnd = randint(_RND_MIN, _RND_MAX)
+ col_rnd_add = col + (rnd if col > 0 else -rnd)
+
+ #print(f'{ms}, ({_init_val}+{row_rnd_add}, {_init_val}+{col_rnd_add}) = ({_init_val+row_rnd_add}, {_init_val+col_rnd_add})')
+ assert not valid_dynamic_move_set.is_valid_move(Move(_init_val, _init_val, _init_val+row_rnd_add, _init_val+col_rnd_add))
\ No newline at end of file