From cb8e6e9b5af22b4f878fc13f9a4bba0e64c3423b Mon Sep 17 00:00:00 2001 From: cutsweettea Date: Wed, 15 Oct 2025 12:46:55 -0400 Subject: [PATCH] more cool stuff chess, woah --- abc.py | 28 ++++++ book_stuff/student.py | 43 +++++++++ book_stuff/test_student.py | 90 +++++++++++++++++++ chess/chess_gui_small_view.py | 164 ++++++++++++++++++++++++++++++++++ chess/chess_model.py | 35 ++++++++ chess/chess_piece.py | 52 +++++++++++ chess/images/pieces.png | Bin 0 -> 41410 bytes chess/images/small_pieces.png | Bin 0 -> 31991 bytes chess/move.py | 121 +++++++++++++++++++++++++ chess/pawn.py | 12 +++ chess/player.py | 14 +++ chess/test.py | 104 +++++++++++++++++++++ 12 files changed, 663 insertions(+) create mode 100644 abc.py create mode 100644 book_stuff/student.py create mode 100644 book_stuff/test_student.py create mode 100644 chess/chess_gui_small_view.py create mode 100644 chess/chess_model.py create mode 100644 chess/chess_piece.py create mode 100644 chess/images/pieces.png create mode 100644 chess/images/small_pieces.png create mode 100644 chess/move.py create mode 100644 chess/pawn.py create mode 100644 chess/player.py create mode 100644 chess/test.py 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 0000000000000000000000000000000000000000..5e838d2cab9abea04ac41155c1e1b0c60c64eadd GIT binary patch literal 41410 zcmXtAby(F~*F1EGbcb}ObW4|%gh)3M(%qoa-F2jq?vNDe6lnzM?ry)$eV^}->s22; zv43mNnl&@aa5Ysq3{(O!O>9OMp;@HWV;t60Q0#W6?N?#tzFliLrebXuoK}ANEe_<00 zt9T3}g_9uRO>?;QXg1Ko&+qf-z_HqqJMK&6^1Y5{ywS=k2R4Z#1Lg~in)+UhuKHK} zz+;FA;t8z8%fLs-G7ME>wUKtYZjEVg1U@boF~mzR2d0GC#<4tU4SvPv!N2^!;!F#qzB^7h5l0hniI|<>^UaA`nkpw~f z;V0gY^4-IrBgR#{&cuQUq-xL2%|Shmm%n`ZB4KC8+GxM{sjW@Ky%Q4>^I_>cpcHY1 z4U!DmfNjQHVGM*7UGT;)L0X}8)oDC}`vMcyV;t5(~|gdh5?!|QDIIL&`lI02o|+-j=jBo zxH7$_2YFV=69+8I-EXw6RER9(B(9l5K zRtx6$)OI$6%+frEj@aA-Vu;Ih3yn+G>6Duq2_INHs2oe@S;;wF~kP+e+ z2q#k4KZ#BA9gS-7KtHvV0N6kh#72E>t|I4xeK}tl8j4J|yEHdiqSKo@#?RX2RLE0k zzljJ}-|<{8Y1m-M@>Nz=nqTbvGT)snG@q+B`MJEDlWg!}$1a(^66IRhc>`HTP)SK? zJ)6=#g*o`^;^j#pfqe%mJPc!~1fQy^>c(7+*`M>*$kh9@RokNlbUi{oee0T#UbU3B zd^_A(Vr`gIC=Va*9Ctd)by5V8)|qHoTt(cgU&M~4{B=D*gD&-#Hg3|0#yIw(iUpst z|9Gj!(V&wZjXh8(6^6QTb-1X=vBhIGhRwjhpzZHcZ+&0+F&F&$qtOEb0z%vQdZEm{ zxbqcFPEO9xU%%pKi^wE!D+)9^)LUY;E8fw4OH1bVzOc?wUR2^k9WFrJaihQbb4w6d zfqB>f7gty$8m!}j`1@PdW8_=Gx|f0m#%+-&U(@d$N<}_Nu>bWv$GGkl@w;=?_V4`g z>&j+TERv98tMPHsv9rd$%oF)z3Cb4TT(gsz^X-|16f(7!mzViq0^QHCu^4-wrmH-r zrh`xkdP76Q0w`1>-}&n1)NJ`+K7fwwGu;^hueeV~n0t(Fw4~J^-Ug4s5*kL*5=JYR zt&v}z7~rlfnGem6X7azHFdAUU^)=<*w7b7LB#AZ3m&GV^zT$X-o|3WI(m`2ADac_t zv8IS-;?KVmCQ*8GKsH<&BjGrQcG_8d<^tKBn0A)ig*|$XmcE;H2P6FW#{8~60CTDg z3sP-2&n+z{M?*y=Q6niS2_yD+q3PZ!UT^(f7=C+Ud>l(IhD@!^q>XxQ0Rb?skKY4^!Q+RFwm zpp3q)Q-PGQm1I^=E-jH`knncR*I9*4M@t++b9QImvT&3lk!-)wZ&R6mG2x?`+5vg+ z_=@lJUh*%TD5M@HxkZgh3GpLw*ElrB62cMA|Jpxdrs=u*8RON5~d#CrU$X>g?lc5$prB56LJHd z(q}3Dm3@0K9ZBcO$DeWeoIl<(FFhbbU zdRP3e{vTmUe^IbpEQQHH9JTfDA`I_~ouKpW@w)q;K?~hBQiJyupk)6F16zsd@Gy)Bo&ALY8qb+zGafich*H~fL# zFEKwI)x?gW{7Vybp?7w1N#>JRRqgu`hBkdv{ArQ{-<}bXJcLiutS&S_#BXR8UrA9h zE+NnPMw#s%ZlbEf0YVTwtS68f79isPK-BqkSJs!!XTK2oLn?WCWw_1W z$V71tRSLP$@o&s04ANS01ehDlp&}v*2thSzrG68pN`{(IN?;&lnY-T2OAqbg@lj>6 zvZ8{GS$k?T+p)C+qQ*^L6Kp+!7yM}h*&VS#UiJ2;+H&Rkkp8er?_Ug&4P#)90qF5IRdq}-S%*THV4IdJJ zfX`TZpDpZB+s_2QQn|^sjJVE8%{cz`yZx8K6Oy!3tupOsfCE~tC>!9<|Lo}rN$y6Q zkl)pQD#Rz6f4W(Z4aJsn_}iiK=g%L{*-5<#Wdb>v_Z9(^0w1A6w2sjAbygE z?ALMdS-;5ZkMkbw>mP3iaCr zt7-%lmx4(08LkOEVS^KRPPD1R+{-WXm@BxDSXb*RCP?9{UqNt4YXHi^GD#c0in;CK z-CgX4OlcJ+FaDM2!L>H!4wRYj4e&)UM&D7?pP+lRL#Fb7MEdT#PkFN7H$b>a50Rn$2Wo5iu#nqRPuz zqZe&|%*+sAkn-W_+s?=XxQZbY3@#|39VPuBsjEx0?0c>M>jFEnn7aZ5RCC_9v=jQ5$2D(&s1C0?~X&D(C zQ8aGHWs#$DIRNW!+3HM)7`orldCO$={xQye*cut?Ogc~JA%vV7_YqQ1s2=@k$!~~q zc6R=Gq$9WkAB4}wG(MEh7N~E`jssewMwOxH+Tr2o_rM6KpLZ(1BrZ&uPgj3XYxcep zUA+6-^1_A!rZo9$k|AnqeZmhom&#Ygn0WwKqp3hFYBY z;6~>XVClQZWy&-_fBE&kXs|x*$vRBd+C@VJgO6aQu9pOyq?>m-YQwv)ev_pyvsd zUV|;Z*Kzx`yr*Y#rJ_E2V6t&Ao<8G$kD|tz0KxJ-AW6KshTCJ=>q~F;RRrT|1qw0L zdj2^|v|}_-w(&s|I-o!$A1#<^nzb`srBYhlavyRhF-D9iSd+yi7jB&=P$? z8G^V*BUIm03-_Rxs!K#sT*{?|Utq45fBiwI-UUt~dr};*w9h%BKHuzI4;NnXT2Dym zCSx7u^(d*lRg+8aP;)sRHs{99JJlkD(M2<+-$`i3A7WNxc!5y-CRf(HfE{y8i2Ad< ze8I(q>%(b3r9i-wi2viI&cp3??n2hAuUc{bIHZ8IsZ92DFEte-BXUqs5RO~%fz#ui z8y;SXG#fJ(CX7A|I`d=C@-oHX;NZy>pYMx`w`JM_E?ZI6d&Gef*6wXS7A26F;T8fJ z%vJ&ef)>nfUDYfB>Tj#U0s__RGp{-#Ct0(hM0f7$HR(N_+r1NXzk9RX|IOrQOPi4D zRK8yw5)c;79>??m2bj%9$Hy7|`rosa8hmdoLb_+trI|j4qe-3Mg-j`aNDzo17E-aQl5deXz|zX98W%9#_&Rq(=HEhWz_ z?CEx!G7K9H6?Fi7Y=uI_WX~V0h?lCYN{md(Dw%wn9rB(GtU zYogbZmFZlmyI@!wFr+tvzP2C{X+7<|6wMIOw?Pe3`rAANb`;M zq@{XwMXl#)a4UZ2Sko_w5M?Ree$#s9=69VKBNG1pVz+<)J;{C3D$P4;vxJ!tlgQjv zN69y1lV$Sz?~7oiaaFu;nY6whYWKPJ_St0(x0%^4g&Xl~o7oS2xXaK1I_1?vaf zlR7z~z`}bz_sk>o!tUb8g>Ck850N5?m9 zZEatgnwlEgSSctde)jdrF15TexA>`Xg2+?3Ih;y*NYUc5jrqm_j*he1U&JyfT;kK8=<44Y{jkGgEg2cvz`=Z7Gg4_CD5H{#4srPfw}}fd4ImH zc;-_hzvD73AS&f*=O%g_Z&m6;9Ef4XX3quolIjR2j=Rh8j?8aPR_(qEPGluS^vl`a zeT#KQRC^<9Z=no%kQ-l53yv{1eMm}?jV1{f5+M5VD_?G!EnE~4gH7Gg2~H)tfcI!9 znI#brk+7)Ry1Ky%{U+&Xu00_Ncw{3ULH4?YUDrI?jL1SU}vkSs5lTs z%=7aoL|0ce@VEo}Rsb9;wbfWw{JSJe3-AJaIY!s&{#L%;A;iZ>l)lE*BG(7=J&Eg0 zMPy$JeJ{(*1OuH40|v~wMLbU+)8BTVe(_lh;jtQ)o7!sQc8P{~Tl*itxIzr}(BW0u zr*h`E1b60KF=$KiLK^|Zfx0a?@qqO<6Q8PWH-ucJJgI|G{?B(0EFkCf0ZA-$arCo; z(+&YlTHe-mfJ9>JelU6$@x;c2RAn$?ogb!9StI3C$SBSi2!_WBefh=ZW$$vkj|%Za z(lBVgoSrAf8{1{lgbI+i)cWb1h%3;8(V@uJ)cL!>SR#$b@^r|&NJ_bVQAM7$D;AKib|QZ7UJ|M5b; z8^v6+ThrbVLa~bVp{F_SLClzr65{&I1~v#!gp-P*3mO@MnZh3IrSDs~7mtdC>iPAX z9shRt^>!S%hS!*(-6iN8jGqooTWzr-`U%0-K4KsvQ-R{(z4@SS6hkRK zR(HxO$}omAIFwd0C&cIQNB6}ty{M=GYZMlY#JhTH>iHI~P=oL8;cSW7flroreOqo5 zR<*6^`8o{`uo+xMBAO`56YhcRj{EymlixiYb6C$I_3@Tpd=Dg(%grW61q^i8B=K8Q zCfM)l6QCHU!QN*ZC{^N=C4%nAfeI@`k9%s|l2$QyZArOIC%A&{T!DzTSL&}0V9H3{s{)|~;zsH@Qmn3aKpBP`&aTj6#MrU&bBg#G>>iWBe z&BYi?Xt;Q47DyH(1!<39I4y8m?<)Y%g2;wIF7BTtASifh-lEPNL>wLg(V!+ujE=^* zU5^#VGt1?+h*+@t0VKW4l(H$i#l~pD+RuSSzO{H#k~*&ov_G+y+I`wx7Eh6)QF{z# z39|T4) zuCBZ(ta>lYQQ;n?+Dm$m6lII6+OIMuJ7Jka#`G5)5p)j>GJo18C8{I<`7TF1X9A9Ny4 zh;z&Po8O#}ei*x7Wr>sL#A)|-KoYf{DJO=s!q-}kk~Cmk>`f1wXY;&*L&B5>O5KB#D|H^rwqu-<|YC^f9rzR#=wjAN*g7sNeoCV^L&f8zls>rF4#!;y#whMIBc;z=E52zTKy3`5KWr^kC_`j>Q8|%y>#vUU2McsR<9(${KJw zCAGD;D>ysXnzjw6a_IOR%SlTo5j(25DW`D;@prI7UXA!`|J+3$6C4;CN_gi>Z!((k zs?9~i(0(|S7lQi&TJL(ioGs#Y_S1NtfPf$YP!wD+ve)?dwACOL?iT*Wfy&K}eHfCVx@ z;EVP;$%r7F7 z-*N58pn)aeEA-tH;so)}_t{AKL*x_bViZQO0`T9ac6wL3L%8f0cul4yCg_at(M(`4 zsM5SH&dxNr2ka-HFfkOO%zvI)8g`Z$((5?>Tq0E5Q=ygR9X7XIy@#O zhE_-@gG|gm6wOz@D#O+cO;y~f!($B zv9n-YXCxo{Fh|;4_v+)w%7@{Ml#Z?Uwh@jDn{ztvE;O)sWD#;0YNaz(0g=+{=H#pT zhIM$`{n7Wf?RiTZ)npv)&GH9ks~qFDBA7Tz|C?^RvzW+8n&stXk#rBv-avB5dlM`I z92}{m<@OJ?k_7np@n8tY0VfkZgbU%enWW8*RoB%msd*e2kaymnVZ)6&F#{CR&8Jz8S@!Zu~Z=_-^BQx_$aI5Tm zOyf7~yJ#P#O#Dk-F-Da;zhmm+8!g%uIm0>{3cN* zk{~_|jgUEcXU95u-{RrslzEZOuuZorNdV|w+Xk^^g)a(cwBTK`=N1>sFm!4yh6%W> zC#cT0G943ke-~%66s_&_lf27{r}yax{t_(pzeEb91-{j3MvP%hNON z)rT2IOOuiA4!`@KYZBxkPeC7&Cxz^nT9a???rIN>K6}eyt8apSba#C`-gsRmFRZ8( z*Vx!d%fazR29u05yZhy#n`I*n0CVm7{Ljvf>do!S^y}U%Qk~MrMNmwlMfsX}QnzxW zPQ&%bLX{fe<1(mbP3+D&w`5I~>m}N=kY2FNY21>G%(1;vod+}?IyD|oBbRYF2N=S- z0RuyUpap9AklwAhKmZ?ZV`DJU zlJWp(-Qi_rWecf2Kp3cZc*CBW2&4_CMHDnNP4B_XM8M-C1kseI&&eq88tZ&-ook&p z6%Ft6r|Z9J3$Bm&Ck6H~nuX&y=oG7xSE7HyBp+(L4wLU}L~Hsm{ViETScuu`h4GnH zAHWQte%IO;l-TtA8G&)3xa8si93)*mi3eg1ydxn>Big?kkn=BURwRKajfd;&W;Kmw zhk7Wzr?BvHzK!X$$0==LxMs7vmy-u#L3AN_~|2c0W=DwYm zZ^&u-%23jt9Q^h5XWN}pM(znUyoe8ffiDQehm{t&GA;Qp8tBbHVTS@?REQr32gkG? zbNBa5wMmb$W0k44wOX7xZY}x<>s6MZ%gVYLQ)1~5a&Qc6px$eD5Hc`+dbn-N-Ab## z)i%UW($)b6`Xwso2i6RJ#~6B*jOuAW7y;t^M+r_zHaIxA=(QY2WIa8-0>HTbRJjAf zQISFo6#Aj103dxAl#~ph=TD3b#M5~B{XtwJrnAcjg8*=PfO6RV!H6}gWg(qM3K_Tv zQG9h;_hE~y@0;UVGv=b@*^`mX*9w$#8>spdT2OnAMC z!~3p3xj@0qF`Rt|M7-UJuOUSz?6#4anWSFZ*&g!l?u~|iE6rcUIg2?Pq z3n;+cKyuc3J|_GpHMHpSI8ai;+uPfR{6QTz67_=H2$Ty2Yiq{EajBD3wCP{6bFx69 z!|UIft)g7?=enuWB1x_TTIczgoL><+-MU{5T{8$GoG+YNEohwGMa9Hu+sG+xj*w5W zXxA8A?oAH@wH%o`^gol5G;um|(XI_aRu*t7zRBrpj-rbhJZ_gQMt`f&5xIoM{D zqS(GGZGQT;ID#^rDn1`^K%Rz#o9=wAN#M$ywV#se?u`4I0RbmsURpr?=0%^Jd~A%S5E@ucibDGBXjVXL!j+}w7HoGi=w2wQZHgO zT|>%UPEJnin>h-X*d+}h7CLPHL1~0m5S}o60F}C+9ZE0#8Ud{3biZmy_4naolN{js zbi3oATiGBUN*aCBE+p5}(Y>HMr%oL7d{o;lW1kB&ixReu+bnEL95mN$av%qK zdcKy?`}a`*D_*{O^;X?ur3>caBGGw<+{()8;7)H!^Q76%!XNj-SmQH1%Uv1ur`Wqa(lK}VwC$TvX=E-lXeZI zD}1zLAsRD9W+AX4)+0HzUcS{rK?p2 z{tuqL2cAr?Y^T&lkd=b+^J$L7DiHhD=OXY$)rREPS68Xuiff%*Xratnt3Ak)s%26j zfDFi|dMjrz1l9!n^KbsnVuf+H`kAsC5RixCfPX!p3X=Wz|FrM`>CtA)VJ~dpqP$Y! zmrODbbP5Kp(shxKQBvyh5zG3<32?i8=pYcoW!48(#5Zu1Jg;_G z5-K;UB2g^`4GHH$Hhs0{^Z7S6M}HJX_TpR5oL)J$-8wnhSkLCwr7 zd*^SbxNzYCj4eYk?_#lnAh4rDA|oTKhcI1bukB3=b81HEgKr^v_15tblP0TNW86tk z0|VHLV^r@NY{M)^pR(nT|8{mNLxppK7TDUc$z2Vt5!0@W9j(;XOg% z*UQFk^(Qoj3fKXX-@9McIi`IbeD|kCyWIMv(r#=yYocHIp5nWAB>EN;JfnzFO%x0g z1u|yp_)(2inFxwFn^kx*E@`z2nV_5ZSpGHdL56Z2qo>Klc?@XT z$;GZ_BG~}I3cv+q0NqmAa{YL@_twxYK>@RfToNLS%mRd!Q!wQ#O_4iQ8n#COW<
  • `uL=1e}r7p4pY=0itN;qgNJJLQ#kj!eA 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 literal 0 HcmV?d00001 diff --git a/chess/images/small_pieces.png b/chess/images/small_pieces.png new file mode 100644 index 0000000000000000000000000000000000000000..e060ad566fb0f2a47025cc3945117815fd2dccf4 GIT binary patch literal 31991 zcmXtA2RN4f_rD)|@3Li6_Dl~#Mr5bVjL6=5#uGyJOPSe9Wv{I4kYsN{DOp*W5&p;T zy8c~VZ+a8={T=6g*6Ep!)@@QE1|kGONYzx8?ji_E96q)qz=QvWG?Em-FF2mJ)b0_$ zk063aQSf{7fP02scOUw)x_P+T*gILXdilFqvnuH5h#&|-`j0!>{ptdrHS~FTKWq1W zQCqr;U|UfOQma#|7bsz4V-sT&2#X5~OX4J{$iKgFk9QcsO=Czi%An7r&%hRm8pK6Z z-_!J>#}q{ogcVU!Qn=^$^7mt3Kc?~VLsy&kj;~DZxk?Mi9qv0DZC+x$?iT1yXtAcg zg=a;;NfG@dTub%M6HZB4jQwjVZoJfb)oxq^9G8w1uIYFB<+C=wf1R zqZZZEDOgcxO=B5E;nYM*GuW1K=8sWQa&phB*RQp_rI^$*4$rUF9MCX5Z#?5Vp`Yyk zr#*SzsD8fM;ZsB!m^2x}Aa+*M8oFFUchOzg|HtN2ASc~yecBHGkNLKB_q$}fqfyf z4^u0ZbgfAOLjkJWtlG!?;r z`s7qZQu3gEWaRGDh7Spg44uh)r)P*;bMGUD@4J7wvF%2}&tDDc=1`593rtBfW6}{H z#dM89A5&_?jp+5?KWjFd-lM1q>xHC@lB#_AC5f=s z2(s$c=qs&eYxm#*6N(}y_}KpH_3H-4buL+ZTOsEr#!Wt@&r?%*WKVuqQ()g|BHzdBM#^J4@SqGSI$_ma*vLV^pVbv6*)brtB)f)DbtgZ zlG3uXU5JS%ix1YexOk}7uP6uNkD>esK1a0S>lBJhV@#1XH=7hOCsw;udYKvetN6&y z&Q6(CPyF|H#bx;)KPn+@pZ??c3-yQAq|U`Vo<$bTATRd(^NVM8JQ&Y~fb~*~d-t9X zFYkVT#*>|h%6kJEpnRYrr+R{X#!xAF{gRs+-@Y%oJL2?n>oWaPnG`ax`Vfpe&?H~SUdT*@$i;_~H zP*O``@@Zm*uikOA^Fze?^K-osT4hQEme`MuB%%jbq5f$U2~*F18Syz$-BI~b6BGKU zvGih6MWgi53sY072*ssKT{%+ji%vDps+zfz>*c;pbCZ2lG9@i7H#=;q|B8x=2BU-j z9i={zx44SPq4oJ*dV7f{i@n+{sZI>B!;5_2Cp!*%KZ%8=fgt+9lIx@tOAVXCsTw>m8 zh9z%rZ{MZEopbg&ugSvS_}<$T{ZeDDxsdZy1ztwFF61j>QE6!Qe%c9#Rmad!6{@f( zjeY%uWhJLi;(Lr(H8n%h5FA1VWQbVk11mKpZrGjsP>w|+uKf`2Q#&XzYtAqXQcJqE zT4LJtzOQ~JDE^9^mXgvFc}!H)&GUbr^vJ8CqCix|nU9JA9+KoFIs0pBYKqK0E;hDs zWo0D@nSQv__*AU&s=S@KBqb6P!Ta9RCN#fz&2_Vnrc|^2vQuQ|8}3(uASg z25L#05jH-8*RC1UQd8?S2L1i>s9>0v?8?mthx373ln4&ilaSNBQSE)Z94;J0d4GRj zY;kdMo>JNUjaE?@ryAq>+L{Y$DD;37bx&Xa@(rf=*MoBBGHWd-n*}>RdOW3lHw)#? z{|>$!wtIe!k1wWUjh~&Jeep_g>-oscj4`wqI6c>~pS0bQ=nsC87#$t0kIovH&GG+5 zKUeXvbESh==qCvt_QA$%WZ-7QZM-&EttBkGHlgc!qP?~KPLg#9W?`!c|4NX~A$;y9 zv+_wX*yD{P&if{%6KsJ8zuW&T_3f{&u0oYxMpy*}1!rukn^zA{c29%PbrmRl2twWJ z3k#K+bi0lnT3wjjn0!adSqWI#m2ThW5E3HiGbsDH7a+SMBh*{PWLY9JNKNUU#7PKs zneMvF1KyiG?sNF61~m4R+~{rB~I`!lQh$?0?WK7ZZrC zaQ_|^A3twsZZ3yl>b+L)y6mkCf9SG=P3cUKJL6sO`w=IKc}Ny%W##O=aYH9O&)frB z07sx&hU``67ya|=FBv|Vw_(dGDSh;ikhA~wB|VQTqTS*sj3K^%01@Uxqi2dEDD!wb zTk+cptG7#Ck$U~|$z-h}4rKY=R?X}J7Lum%x>XEqGnmbMNAB9SHAT{983Z89q0y94 zwT+sN?fqu6^0}8QLKW}*`z?ei5PNuuz1H)SRyNb3ew$f*)=iS1b9w{_1ug9%4o!3R z)X2!K%UQp}_#WKD}VhaV_-lpkwJuNXCaxg4`3 z=3S&dvcCVQ*4Q9#j=}&R!3%qsIOlSRox3qxr>>;55VRXtIP&0!7y5^EUQy9d3Ozb& z^WgdO=kfqneExEfGD#(}b8yIEQg;g%5g|9Hb~`g-avffM@aBm|M82^d zwy;#}*%=l&qF*f0v*hs%vHQ^-C+f(fmZX}pG_;_k1lN=k@wjyhD_!HYnge>yAz7^b z@6?~4FVoV}mb|^aUG|$vFun~Bc=`AOY1+hA7ObC>VWi-q@sY8}9IaxL%dpCx|J%hv zC@9Ms*RCBy#b|)*zwEToa>_c>Wynm^yUKwWptiB-u)iT%m%Qnl*KfpUszjR(%_1)2 zh16!`S_%|ohWsgM@~qoW7PeZ=P6tSoUw!Y49PAq$JU^~$JuTf^j1eM208UImnci`_?o}ZoKVAM4= zodITs+@v7*M;IN=`XSa98;w{3JePw_C^xTn%iEiAEGG;dazE!DpHRo9M}+!Bm2{Pq zm2of8(gLK){F!PN@XXc0VG+eDAyHe~*mzt!F)?AA`-VKgM1Mkso~dVpgMAc(!cKhg z0v8cxi2LTxqJ)!Q*3{zjY}&{_Fo}$^eR&ru0u!8tKfAlr+#hL>*`f0_TxMi=$zx7W zU;p6qQ(7}#0rQpz!gM4>HJ47zX_UW0Wmv4T?(>4eqpg4M-ak~U#sabXp@O@+yQa?j zDeS&fp`tj(>mwaciNka>*a@N)*oH#H(@2UMLzUdC6wsWF{PdyVRw93HWzIh*SG+|sBTP?Ezl@zrELbn^ z;^f51p8xst9flhog(UJUGJe~+!Wn@?`eeo94v()32nam7vwOT=-lwdr%=TX^^yQYb zW5)~`|0)6uv}2$qB3Ig^Nc(La3ENh(MJ(D7EP243sn4R6bV>TxX3}p4*>eHZILpp! zY|J>@FAwRhw&^FriY`s*TS8-5WMl|sdIexjsIVZM1h-Vjx33&Uy_*R~Vo3{HTXV57 za0^p0@z?Xv#$>HK-HZx|xZAh3!+=``zLL0ThoOPjIP?hg_)Net{{f~W4LdfqS00U2h9QA-MfQwLj76NsX(@j+_vVMy9)~onNJS4az}>IU%N) zQ|iot+Spt439D`7MT+j`F|kAX3QWt`5_(oo8I-u%tOu(c8iF8&fS zM22Os3t9P<>3VtjOM0gl#do6n2hv2CN8$|AB&Su9r4_B^2KHoE8=1)>%c(P(h*J(Y zQ~SnCUNagt`E00(92}?rI;3qz3_{Ri&v*A zt@aLPT}PqeXkQ=pCH`&=H{q4^)(1TkeF4Sw(n3iyig4rcLe8VrqMU>D%@&LCclU8v zc?qyqz0h))8xOtb7lu%n(bW(5NdLaD1Ft5K$sbVZU;gVVNtU=%0 zpb!3f`I5=e!=s9xneVe=$_7v~JzjLWh178DTj$yLnt1YW-@X;FsZP(z8;g8JoX}(S zxwp3xP*oIJi?5C5;Jl`1(_9WY)a}TX6+5}-3hqV6nv)7gLIBKeN%M!Y{*&M@K@i|* zfpmy~$$S2zFJgnL2rm!MGJ`W+|CZ-k)mRaW>}pgVA=2jlH3yYAy6OavHJ$$a`5p>G z`B3}-CpN;}rEZoO!>yS?^Y8C+j^ai;p66|5b?K?unVFThMcGM+Dm*35(o}|I!dV0v z;)R|oSlcOSJ3BpD1%kn&dDq(7x`>%>?&&gJeJb zTckuqlaMHF*?k)3n?Y$rL_`BKoP@a&NrL39`RZ!Ku5O7ywtqrn5PPuqFR<^FzNU2-y0W5Uof?pTR@;x3SRx> z%a<>8mR44-P=MX-7d?f-y{HnN{MD;h#SF2?A~{CB zyFK{5<7Y-MZZ{MGUA*Jx&!5!rr%Th*({iMA!WRCPmW$2*{r8_yMv$}viSx22@+MrA zU@iA$h@YifpoSUy6;E!r?@2)mV~lMRYURXGOGJT9a1fSz05LLMo=6_)v-c zHpK8&UZ1}ho0zN`v>snVm}XZ$!U%Ex;~bCXCyff;?=|K8_xghgxdJmCr-Ef*JKUU&B@zmq`A@PMhavO`u^Sf z_b-i)jp=xM@6!hYwtiDm66;d`9gqHw`I{#JjabYj(Z}>9WiIYbb3kR|7`T>y{ff}y z3s$I`84Z*?O zJl)gVSubz3S+%EUSpRwMSZSzQK3P4zj_ZtWmbX*UP*Y<=7AHzg-d4Y7Wq(&%`U+4B z1{s0}Q(UNhX!y0eTLr!KVK-FAE6DumxTkr$UCw~h4@u`4B_$<5%lxQZ&}FDFiDUx* zEFNpq>DyY^2Q-^C{>pGnBmEIvqdw3)e0>#Rg?@i2m&tI8^7~^9Z?xBXY26*287FN+ z)10dndxHiiY_i*J*mq4$P3!DjoSm&5$c(HVQhicgE`Ph${l%qm^>wnB0CQeLL;A(* zFSz$Gv*E!}kAGEI$b=&wax%N1wR)qZ;P-TOB%)(u^SVTE%kJONPCHN;Re&x<{dJ|K zaq!Tbpj)63Wn&KHy3bw1gY!|1R9t1bl%f`L0j^q-QqYF+S1b{B@?h`hxKhTEew%Q8eJ{HmWSOXC<*%i_x!JR0WxbB{ zo0QX3eoLQUuo0eC58mX+&lZqS*8TmTQqSiAsM1&z09v3C2Nvg&IbQ=gxD;Ey?yE=4 zr z>PhZM+<11pzWU#7mOS(GMj{V zMMX7ni}~C4qhZ2#UJ0i%%b;A0xu4M3nxn;>zPfsz1RwxDa32rkx;&rtBox;7uLqeqt2lL)_ zA^0v^ZUQ6j*$w^5qL{>TcH1^86xR|Renc)Tr5 zGr7FYuN=!7B9J4{Fp|rHxAq5YjE!fSkQG?d8?PvhP6f?A9MQ}@y3In7_VLSNrQ~3L zzjfb+=$^%|-#Gl)vJw*eQb+*cFD~bnW>ky`)ZuH|3JUFNODkIcUcctfKzlprlom|Z zde4oDp3w@|=v-0N)?UEF%m?ji{`Y2i4Rtwqy2_gB`K38Fug%-S;on{AOt9o(lTAMn zHC?Btrfde1k~gpmc2?HCN@r(hFF-DlEn>J{^)0V?{DJ2sjysSdxt~6LvK>AWNKxXWr=CS@*~hVDq&8ha4E1 z7n|7{=yL&*xEm&_fKY>xJrF`723!sz&I)cPd`5Typ2o+;F_-o)(|qjdIj3Cn;17B> zMQ!)&zUejvattj0))rip)`)qrg4yiLRRuDwM~lSW?c|G(x&-hMy&|n~qtXdWUaH9F zIXQzV6bf(5@IZBL!IF2JEF;~of>sve&`6HL^genQ-tp#^($+$&d3e>>84FTXU7dp~ z4-LXeHQMSI^NkxfLQqIpyRi1Y%C#3SUJMTx+)fv-7kPP*?Zb(zYJ?cvyEm5CtSto! zyd5+{91IAUzM%O8(Cf?jzVSco`p79EE89#Tjq|k_HAJ87w@8R35AKS2IsSbsD-O*x z;U{YLF@d`C=a(b65qS_OoDgyMMWqIwzJKxGz|Vsd4epMHhD>P#g=^icEmoVv|J0KdNlu;x-e8AyqINfY6?C+zc4If;`J`Ra1$3H{5k z(d#M~7ni~dR(T~Z2by0w#XS{<{KLVma(7>qj=Cw)p!G*aMnb#0yN4Dqi=?#$Ep22{ znHt=MMSR6p&1N}6jK)Su7YbzE(aLba#9O_hXiy0(Sv&)?cneMe;eq|LjgQ=I|FLsi(mrT*){g%{!OSdxjbSAi zE+X#34{PT~R8>`1#B3jCF(a1+A4Fxk+}+!R0@n@x4?c3V;TjUt;JF;paVQ~&M8(8( zKEJB>rmBjHJNeo4l-tY#+}j+Kyu{be(z^L~*sPP2lY=0I{=Jf=Wk8#K21WZX^p5XC zd5WEy+@o9Q=?~g6|rzn zNN1`3dOx!Li6emk8hoO4!{lk0=5tVM(q#NSqO=lvuydDB!r`?Q3FPAxdBY+lg-|H()I>`A>(Y?QpZk|T63b|}XtPR?#Y2uzRCJI2 zKMU|PS$jJngg5gc@nwF3r>o_i4NN^xS7>qa6tCFeg?+b0*wi=$1W31<4_VgM*T1_- zWfS9t2}%f%=`gUP(F~JjfdYtOoXQVpL>Nw)R?+BFhF%L{0APejEa>d zR!9QqKu>N^Ntw=qN+HjRfpdVspB^@Kg9>n<)Lce+Py^4W*sncBEyCaFtZ)2LgX##jxIk_vD$ z1YyGfNu2~JxT>PX7~%|2ZKl3+T*9@tUFjICY@krqufEPK1Wmz>B_Zlf;VnV z&rVDngh}SOZ7V$|NASs*5}&jPVbdF;cYpPFh7*3KpIRL)!j{ib9&!U^{S!h#Pw!v& z?%mp(sRGK;d>2#BCISD2qI{KQUlhdQKBRJe@|65uLUX5SlT>&`ZNu6xO$s@hV~yj_rL~|CB-m`SmKaG%m)VB zp87+fWkSTSUnd`1-?u%s{O=sp&!bEtc8zY$PcOg+j2M+WJo!4+Ay1JkRgt z__c9DWXyD+8;)wW={c_a!zy@RsnneESh$@8@|2#~PY|pp2*?2lM*Ys64p^`X;JiP7 z{jvlIG1c-|`r!A&BDJ=zo}PmGdOG;7+kb;t^K~*{AKNlrTY5>a+?09Vy0FEAxHZka z(jgm+wA&N_k$yo-O|2coxX-ThALucn&%CxUhcoH+VUN)o))XudL51hNdb zl}@Xf2kjobcS`Q3tof^H%+)wT&rg??#MRo2AK2Q!X1yf-Us`ZJU- zdU=1kSR`0~ihzo^ePoJx2B8VsAN-#78djzqZdlG{tonEnbh!X#fVs8I%2VGv&vuh| zp1)0+HqexweRiVss%zoJRTb@_nVCDjbKbPl@prLV)iPbL=V4!nB0b0u)Vfg6Fl%fF zSuOyuJ&?n>eC64Mu~8lc(Hw3qEO0au6BGA75`A_Nb15I5urjC@g2tQ6-y8MDSL60NZ=%sDKGqWo z#lHT2uKa@&+`!oeI%jF0->k@!@NgXP<9@$1y`2?v!Fc5GxXQ?Cp6m zz%|GUIr}T_yZIpFx~thrIZMh@#Q2pyvE%aCE~$#y3>7Xq=WTG61xWjVUcH^+Yrn!CHp; z6Lt(jWTMCWf>#7@(X<~}qyV~2IY~-oX@U9zCMC8_^+omNP9}s8 z^ouWDj^VnO;%2JBN&&J42nBYIj*9B)#2?Ir{sqf%s@;N*AsZ0OaXTJ@EqIUyFsyT7 zp7I!SlI&dH&$g5tQU9bvr{Vqh@#zch}e11>e;x!8n40wx&k0HTRjNpx4MT8%A;FPtrupq)VU+8|%)Dn}Dk|Hyy&+|I$ zooIJ=cikcQ7uC?v(D>zx23Tmn%b&=96VxG;r!6Us}=4WSbuND^- z<@Wyld;Lm}eTjtg+ihQ4*86RCtuDuO8JU<6BVXZ|SHKa6@?JHneisB};efrBp}^$c zN7aV>2{pB~vRJwIlY=8INSUv|k3aw5v$HAc##o#-?IHlE{M-+?kgG4Q2?(iT&R^^Q zlXRMrU!N!~fFu?r60r2bzz&qL8vk9JaIU^?m|q?9up z98q1ZRg8_Pudi=H4gLGOiw!1F9>}u}&d#sE+5PdutQoa?G&DA*1O+lzGgERH467-l zG2;8iC;KCGh)VpA15lsCDSQ&F$*u z1jb6m7c88g9rE0eB#^t!K!CH-d^n$+Cn%z)rWR+MkRb^=UM=#tt*)(0 z^R}=s9ORo-@OblZi7yX<5cX{OFAHf>VIk-7czH0;7=e82zLa6#g~zu+^WgDDZ?O3p zrA+Vdqj(#WuxfIOrA;Hjq7@NwuL-CWWJY_f6lVATA;b&Ii=5*hn^S0`YvEL5`_+{l zzqSK}!78hSO+cXk?W@`=6ph|@Oj$m+o}X^n!#-ZrY2eVC6Up!aB)(p0-~1bnsD`rY zKnO!lV)gJKO5Ye_cFf2%K|wNRZ-7GV?AA$N>s+r$T)$3z@>-M{Y&w*us2VIN&V^5rd%I9Z9PHM_5O_pTE1(@jcHe|J5)A?N4Ki@c_7{}l$xr)wJH93U#9 zKWS-bJQ^^@&Ivhd_&RI#0zk`L7zL(7Y;8DLar~*~?U9+;O-qZc^XyTB%**JB<1{^WlfsfE}{!2_m;(zGq>qpR<2kBlkiNA2l zng^HrAf(wy^VwP5bZ(jl`y1c2Yd&x$d>tGeT?YmRE>Djz*qICZzVB3FsKy)NpRQpS zQ!kKyEd&R?9nSP4-tPVo$RoYGbw2m(lMiJiHxEy1@b4)RnZG}iLAtgqtD88b=GFkS z>?i0zL02J7bgO*wB$2;w(C*KgpyAT}9T{_M*2en!G#M6ho<311bHg(;(I=ZtFaJF~dx#W5f^NT9 zOtE@877{YUCa%r6#p4-yf<=wSE*{9I z{#2!pQp^+>t{FpARra7|Ae!R^7%Og#dhrll& zNup?MoPi?`wE3hSViaq_3@1b=%=5&=?T6OZOA-+5Yd8Wgd1dP7h#t1y$O<||z=Ycz zEZ_@7C3y8JJ=QmzfM8S=$Rka2eF<0Of~Q@BcNuqHrJ51-zwhemQdL(69zb0I;xy9@ z4;+>Rd^o9M27YLJm-7d7#|O z+eZG*E}U@-fUf(gx7R2D&WUv68*JyMVAopX9x$U+!BxDa?OYu|q(Gr#Zk_<^JZ)3G z1Fi3No@!0_*}5w~^J6Ec-|s;#A4Wm}#~<(Q?Xiedxe0f7@5%tA6@hEG15bl48Z|P! ze_sMm9=mZRWM=;6_>u?nz4C8K$k4cfx#mT}13bsX$@Puv2?Y7PccB?CEiaEoUI|u( zsJEQf za(Q+6x0J4Uuh|`YE*O)hpbKeBecRiI$djH;J>^ zTu;xF@p)|x=D;K=fJm0c`p;9Jrqdia+oH%#X=z5VtU*9ha&qElBE!$m&xcGaE*$LQ zafYOBbSQrxa2Pum7ZpWCYz9U~MfG(^IEqV1}n$&W+Q_(`>j(O2Tjvu++lr7 z7hAJ=dbj|Y3mVg)oh}dXhunUC`i~Ly4|`+djerY$3+MK z3m)mj>({UIkmw;_3H%s24gmpYQ(axkQp3v7rj`7dH+94=bX8QR+N+5DQ5nnoE}+LIxU4O(i2EdY{d& z(_HSn~AJvREa@zH!hR&waaA;zJJLF`e?lf4u$EG^3vhqJ>|CK(>k+-5^=XU`2(k3P* zvYMKj6~?6=Mn*HzyQKdp3yNeUEb>xtFXFM z#$|igVROhhqQ>Ldyd98dEf7+KkWTByiUwGLq+a1Kyrq!qSuy9M5FB+l@G?}lUoL{(b zMe6G6(}Zkgea^lc4SmPQ0T*=;@^YPE6>RrkU+|)XG$0!sY{31T=_1$Jf$~i?`3iw6 zIr8x9MLv`Efg1|6<}jEu(K&-4MwZEHT{~315Ce1bb2;ECHE^B&Gb`V!yZp`n?D>;N zm4-i?0}nw3YnrmGl$nRkdC4~W3ZA79jES$H)qQoYo5L%goGohaPpckx=mKTD0Wcmb z8yg#J=LpWypY&`pqEfQuS_m4qHqCANRZlxr8JF?^fRJ1CKQp+ngkW&wgW1*s^0n`i zZV0{DC}e$KK;k_V#5;E|92@DyZ1(OjY!Dfz-bW}%@Hk-E^D{Ft*4#Xpg_{Rh5oweX z#eXlevjsl@Ebv=smSa;iKC%NX0|x<`vDKd8HOh&A}(r;xOi@D?WKI)TSy+(j#Z@=pL zspw;=B9BUY@_c+~D}45d(S?8S*c`5Rpp!lT8}MCm@qd|_vpqj&AJ!U+xO|lYMhzj+;RsU3dZ;hzl9>W}`%FXT zuNg>U8%V(C{tG>X8)*l)83#iU>^bS_y_Lxnb5A(c05So@0!K<49OP)o1Q&TqF~J#Z zCT~+ey@x0Gd&>>4x8SiS*GH)@HoNbDR^Nbgun0k6Z|EB&8w}Zm5#4mwnwq3gbKNB~ zOKU4D-T5(LQt6e|==3beWNG^QH~a&&au+&x#KM*wpL&11-F1ezscKvDizuxK8f`o| zHRTt^-?vq;{NoPBqv#_}a&Jv-etzh|*23V@&z~RzkuG9 zuT>dU%F0dSuT-So>I^cL^!!sOIE;5s?^hc;P_9;k7W8&{yua}m*rTR*i+j95rY+wMhKZRkF_yfmI_u@1rya`Cr#4D zcw`sIhwGh~rab`8-`6_Mc=R(XXIcO_*8_}NqBZk-0&(nAub8|WOk{xYJ0V%%zWORP zV!m1|8m_OMogE}UL}2?8{`~O(S-1ca(UD}#SkeXmn`okwa$^NF0%h@=N5jSZ4}{D< ze*DNPD=S+QuCArk4V60b1)qTj@XZ?!1P#2Q+YcRbF_6oPrfUct2erIKK3)XS8zEuQ zX;~Sb@{BAJTctqc-jf;9&~23ByHb)_#3JnT`hNZeSqO5rw{Hg(GQ21|AR3NwzghWV z4x*p$OGRe#&R#mthn^3anwXlB`fMj|mYJUJ|6+1o))s{PiT=#%#9tB{82#N43vq@n za2#`>kn7*Of4`aZ8?P1%&V#8=pS-}n-vrF+BpONekd0qD$j@hA?GtJp8_bYcFA)_> zN#KnR$kOT&{PkTzyxOBr!}1aJpY#+9G^D17^G3RkV<#MP`7U8m69K+JGb4fxKdG|m z33K8kEv(Ze98;jh3bC@YTcQ!qRFb&+5KX$xN+Iv>f8%0#zkYq-G}lw&??_HJRgLsNo z{*SD!Q3x2&GFnBRhN&c_0IQI1pdk3I2dn2s}i`*XEdr z1@rR1|3tonC`x&_5Yj5;^V^Ww?T<^@M4+m98(Q?l+T~@st$uRvM<16a4(}=>JuD1# zai!#5@z~|RPhT z5+1S5_QwMXc=0kdWPGJa1Q$2AjWK_UBaXtht*z~ef41v)sewWFm%M#d1lAO)Pvmp& zCl%tUC9Nsx@&jlHbaZqizLzmgu&FLhgnWd73u)joDJvcgAEh_zImUu*)78LU(z>kt zpBNp9&Hh{iVunZYUq;3ql>*%q>uD7wu&BRXdAz3!6HAdVX|tV$CA6KL1t74F1DXYJ zO&-7O^I8yZA3kKutr_3Sec7?1arXz(W+5C4m17jLA?jM1n$|bZ2k#ge-V{uY$&~gj zyP(Mm6ydt2qBU@V=k3HTH)d*Ud^U_@nWZm-{N6V9Ru8)TladntDW~@?eCeI;kZY1p z;slrssdfseHMgLn9&FC_m0d}WQ{h+`8nUpqUcIkEG({4?hoP|gkMpgZi!d5DOQai` z1Ph|1%S77cYj8uq5V^R70gR}N`FLW47f~_ZbIytBkG9h(x%8|z^Dk^411_qIG zXFCZ&Fe)kZJ{%TQWBC($&>QI{45et4SBZg$OU|E7TAbvzq;Y)tm79r~dBQcLUAR5& zVJIk$rZ$r|vYk}~2(aL&D!|BaXuAd72(zG4aO)Ka+C>12aUXl5+W|g!Hzd=NvF$hg zf3JPLJbE1!?JAz76$xGjjE-9@eteb&9>qn3?Qc;|a1z8Vyu7_n7q7+Qv%=u|e<1Rv zy-ZIh^wA|WIT6dC>*yQ0Z}qfii@zDJ$L55I>x+4qG%bX+<^zUduDBeVAu#DAGZ^xy zZg%-iqtYW=+fkSvN1ljQcjMVa9Cicr-P7_R8;v>Lb~x5Td!(1*6O zF8=r-;`vMC%gYZ|RmW-Os2ftLtv3R7plsh9}VOG}z6m zFnz#r;oJs8H@z6?w(F_74hY)V5kq1kLZ>?a1u6X#x9|sm&3Y*9$vT zqopRm@!L;@jIUyB+Qy%K+_{ICeBKTxNvn@hlqGr<)0G8Z(mm!%Cf6r%!rDF8*4944 zHw%LaYlbb423CU5c!`qZ@L7c{toQyPL*4=W`sUrcd}wQZP`%qP+@_`nf&)KxSOJd% zT6h_tR8+mEs+nwc{h>$zK!+8GPA0=Rm;zbbI8375Vx>rn(bHsVj01t?AxMH<5NH&X z&KgJ-e1C({!uj*=zpImrM*NqxBor2f{@2JB&LYFG+bAisgZ`+&O5$j6Q}+nI+~kyL zs`*G*vpzV$T|Mz@$xSNlY`_~$_VN9IBZ?odVr=Vh!M@V4Y_`tTd=c~pas&eFh~`Tv z-3)lQuP)bJD-ET9#K2D=H0+B~@f-(zKJrk_fQ_ht-ckKBGcyLN^ZFPm_W#m^G|(7Y zu)mS=3Wj1@{PAHp8jMt5V9FC9OKWOrYo}f`-ugKg2DBG1jF!XM?4gfS0ct&9SqW(b z=x$ihV!!!kFI{=3S?h$fsU;~vp97N$TAFn$4FS%#!;`la6^SsE76ll<1xgzh0_J~( z>5GB*0g4{?ausFeEIkvGb@SHX=7(SF-GMB8yuJ{`0pZI`s58T3V;`?yzs^BHAWW+u z+T+gVN#AUyjim8VYNc40f#)|{>qy%diH{+_d4ym*Tu2|r}eziFdfF} zvu4N+(t@JI?i>ZVH@;;g(UUjJb{D5-MO9PIf~=r2u8`}0{< zYDphq%x{QGDW7+ysq=2SY$5>ygeBugeFGf8U6{***P(TT9o31G0ObbI>9q^NxM}#P{SNDctMeX?S8dXSe_c^C_1%laT}~|64>ZM`$$h z+Dg61r#Hfz1qJ0&i?O+41(;ZZ{7`~ulISWNBPs$mr-y{o=s@3vz!tf+?9JJ%&B9Up z(5>ab?4oR~%~BI@2Wjv3=<%^|5Rx#;9TW0Ng@@1V7|vsUZCC{@Ai|9Q%heyjV9F~H zEAZ~!cBEiroJ~ti)LC4h8`i&oNBJr!w^%^fU77Qo7X}mLXpTx=+l%1@$8SAB37s%GPeFN;clv~TXPi4 z*izYFBN3NSSHX-PjTCGDn0#JEkOY3mCM2Y0Y1u}4*cW!GOMCtz*_f&X^h2jr_sZwM z-(zPQn0h6geOdrr#GRd;C!eccn&{~z2Osa*7k7Riiu4%{V_;#~RK0WO-6nMAXPw`9 zONxt6jP8#+N9Q{UL7U%g%wvBvzoCN)hZ1CJS!p;zil=B}XlNMG`{)i#mccX2R_5ig zy)P>pfb`}gQu5P9?t1c1fsrh_oN6B3A3yTK0~}OTR1hWi-sA4`Xqa=6n+Ech&--~8 z%HAUM3jwrc?fbp25TmNf_yy3gOelStwm7v@n=0`#Mi#F@^v zY5pSW6Tey#vwT=SZ(J3BD%3F!Kov!R7VBm2E)Qf;YjEpDaH{oc6^Y$Vv5tB7juN2> zYyUR(xlTRlC$LGa%gr>(gwV%z@xs`kO#IByc+CbgbA#3|xzuKqmFjLWeI~ya!GMwb zTqPqnWim8~nnL}~L zzEr-oyUDf$XI`~wF56C=;m0NAXZkc;uz6}JOL&^Ccdz#SnTA)d22$>Q^Rq<HN!wW>B z!eMi{m31AgjmqHPrSZ0@Zg`)U*X|CIL-*x1$2j%j)~y%NZTb2Oov88qaiJ!w!xSuAnkr=GDvHp?cODMj0tF4lQSs~!{V}{pqxFWtI zb~lWWEMow-n60lv&|EpEk^HHl+0736xy6r^<}&s62aJA6f1K&W_5`cD*I1wRlO?_$ zkho#psFYttRPD=&{p@<=PY})gywdVeT2WcpgkX#dIfZQ6k-qX6yBR1X^zfFuZ1xMI zh@*2{L;pI4u8(M)bCBbDT}51dKcB53CRy0PrI=9R0n$XU`;h}XiDW-Epnd!R8KtlD zj!vWYf8ATMG+=)FXN~@Y_@KGsXPBww^JC9;WxZ&=Y2@$hs~^1G9?5F``{k(e%k(%g z5fbVB?k0}ttfU$t4srC{Kv)!<4|R~!9@BE4#%w?C)`m;`QmDM5U+wlJWbDIk*oSN> zRonx2Z^c_~*DqE^eCKcSv=ccbZlBFK_H1-Wq(464=lm7L5LJ?i^PhBUnhKUVtF-i- zAcxsPq@am5nY$}I?@=4auneujl`u;-zdK7)--WJ4z`z$U#So+h_eXDp=PU`{uGt z)@TJorwzhF`}XhFv+a(g*3Qvm-PXF5oxmr1n|8CT?}a?a^X}A1=~QpYA-AY*e`0X( z7TEgNaMStVgi69^wAT62W^n~oXW>wG>i~5%fC$d3EE-1Lk*yrQ!`tT+5nxI**t1HFR;S>iij#1bEqANUFpli?27EZ@6<0?G z@qLcBRcD2jGD5eN{>6p+xK}G*$GpLgF=4eONCJ>m|E|02{ToMk%4>HW`6W`VJ2wYp zgCEb%2D=dt(Wz*Xj?tZpeKT8Ap&>F`KSZWx^=>RJE|&CG#Wk*=%iLpWyW(}3pEd-` z2rLw~iYW-RW`h6m6~C`Ynh-9IPcbdt`%}_7CSU9&@4`P6O^1|?HAa`Tbh@}__f|Z} z4dxK64`q+{UF&Y#Mkcpqc6qIDxK)wet*G&<99dqgkM@mLoNLys z{_$yxxz8Bkij0WJ1C?@)^SyPGt8AHx zhk1&X@3CM&>V8AgL0|gg%AnNa)$%|ee|CR`#~IsIDel{E`Xt!e=`Y+`@d)E@Ksx=) z2y5BH{@v>N=ovnBcw%faGP>8?x+}bXo0CPj<0cDBQK17k~bVK!!>C|0!jGqWD{2L}I`n7wK z>Vdl&E?ly9v4kQi{d-8x9Hy4Ko8B=_uwU+gGN|?sdrl0$;;yHi+P_nLn|JLhSlKZ} zuei_p+|AQ-B@80)mi%x>&kEtkJn9*Ds@!5c37Na%&7Na_Tjk&6L&31uul0dll>(p_ z-bavU^uBQ6!v4}|)k!}@efrR>-Y9{=e($X-ndP9!#hDnh5M4v&zyw=_?pAHkU;DCl zuAv?wvV4u(?;xdf&I^pIrSohy_F-}q6Py9Yh`nuP)nQxgFfnz!1wBE$?`%Ng`n_wx zj{W7vo|?F?o6R^M1NLNZnkMGr+=Bu$(|D5MRVVBx$wsv+y_?39I#}W#H1-zXuGID4 zjxGw_UQE#U=a`b1+j!d3mw6(GRWz@ysEp>UA3c+OScB{Y(oEPCq;hU*Qpmm?eCKmH z=#`Va---3Al6#$JVzu%j`Lge-ymfohf6rdLcn6iAMDrv)jytm$K8-%Mu~l)~#fAh~HH7f5~3II;Sg%MlELl$yYw)pOeT^ z>wX&Tr;F?F*q-RtGcP|-=;brY)Dz?74TZ`sAzeCzMKFmVWcGrEJP-MXFaFn+HSv$p z0?!PG|b%PBYbXV1>V_|rzeUo$$T(YLPt>Z7CGeq zRje-mpN2bTJV$+Pd9prCytaKlzut%BziihdYMdb5{(d?KHq+I++jY~_9MshlMMQ)e6u4+P&{8yRls(a-crHHWNB#o3Ew{-@I&45?$ zr?`K_hngE6BuLek(og*f;$w$p=9RYo1uJJyjfCtbJ~DhE%|NSb(cVPYN&qjlJVBAY zMs~F?(~AFiv(#1ntm7#KO4$R8_}BYVS7esY1i^nj^+-shHSllGQsf)RR^J8zbn?4T`gl9V5#~EumJ@ zul5L}#i^3Z3-oq5UduT z*AJNavZ8)4ct>Q*(z25f_UZ%ma&vXmH>-b}cT9?ufcVMY#x!C+lxx{!kI+NcXBEP9 z`-~JMy1IR@exE@CD(GupPc&Q2go<^*c)e=nbQdpYipIFDAT|txZf6|h@6LDfHk#% znAm%1*OeQ{T)}{8Z+PMYSb^V=#G#=fDNbvCnaFH~zslKK+#dt?nAliTPEC98)Ml31 zg)_HSl;#;tM{MmglKDvkujnnvS-zO+V8}wM+1m?(6%2blG#+5KA_Y zNUFiB&xQ1_G{O!h`Ee{P82Q&0MOO`M+QfJiaE(O8Qtz5L?s>@9enEB#{jSt`Uo#09 zi`d}ENCqt2gqJ0+iOHFu>3kvOcrS&yUp~La6^zVo5U<@TCN=bzDKBKT=sBSn!(&-3 zbDCc6mfhlf6)idm4TyljN2-uaf|V*5^fZXGQr?nXCqi3m`T3&nqM~Zm!fe?FNDz93 z_z-Lg@$-^OUraId@EY7Vrqm~a{R;8I9tA?i=w zzt01;v-KAKZ1WBlMaT;Nck=Jk2?Ibrf}j9t21aX0HcnRG21x|1JqrS>EVGW1XVoL5 z+p3k6zjs@Uf>YoPOgwL+6a2Yq?)pkjQq%tjS{iM%*STF9-5}jKKR6JU(6|S>C$5^g2}F*7P*ohJ1a++ zd}03EUvDHqQT{x*pj4rC-5I9%#}JH=1Z$snhR0@n=B!cA{2^ANsNPu1b(wWF{#K3i z>{R*ATubcSmS)voimvH9bGmdp4R)qasCwVmx$E1k1|%#U4(qTNmjXe90Bm+`_?|I| zunwM~?uI6y8gibCD-d3K>fr$k2@Lx39%nC@FirZT=v99B_)!zFKHA?BJ(vJ z`wX13UNFud9&9hWfWY+e-UaGG*VU1m5c@xe)h9B0X{B^TH5494&()M<(P1U;MU#KC zbmMsLz1o#)UKSh#_R+Kri#wXEAJ52=T^!iSkK$!m5261+7Qnxy-c^3l2l z-o8~ttl^gvB!GWuL#hCxW9HNRK9#OwyQF$Kzxv5OYND4-c{D-w{U5g%UUTJrPt(h}eVS@HBlSSDS1Z%TCcq-e-xT!nfe1jo$Y(9N2(x3p}8LrLQ6 z;wn7AI^8sR;DpE0T{KdLr6Uzq_}8o8=gt9!B^6Rv@el$C3wsCAysfWs_W0rwb~Cy* z&+#~e;cu;F)%>q3uO2*f*K_yI?(A$5ZFHv$I(CpN+VRX@mFgs7U&fv-Cxpm|@*`TW z@nHHn7?n_{6y(Wy%7%gdh+YziZuG!2_7Lu*tl%t@z|>pD;-6mIWe5C?7K{|N4?(&v z77Zy4n7FcH?3@R!H71T30T&9?kYe!_xXIoiU9eOMQsh-s@dV5`3;ca+aMEg<(O0nE zht4y~QxHD;Ldxb=ts7Hgo(Sv3^;@?dSWLfV16vUoBB{1xkR3IyN;APEyuM$%`>r;-NgSo|npXdJ+ zz!+IzJMf>a<4hZ4bge8NU+ejZVfA|cs7Pd$M&Q^+#X&=0Iz>v%JpVe2H#A9&y88{j)6Olzb*`YI()M(UqtQ^#u(#QS&9d%>qNbC&^aIYw$4Un~Ob0v3-S^v4 zZwE_sgu z$01%1Lln?mwrBb2^S&7Bks+*Ce1-}PA%XwGFR>)w?3Ci9e6wn64R;{7O=Wzq+}JSm zO{l4*fL2F_v#{cbY%~|vx_&!T)Y?u?xwhRd zi~TR*rOmQ-&+79w)br0!r$Ltl&;j2f*Vu_&Ps@_(-E!Q&Y9Q$?$P@OH##g1)qlT$? zvdD9Gve{E&h}cfI1?p&xA;*Kab`(a}*>{vv3Y7lbGp z|1nB3eNYGip@DPenv@7^y52*~@LAZm#Kh{Ya#$SfvR*%thFaJ?lhE(aGUy*JZ^(yi z3T40QKB*wH*X}l}u2W_2By6vXoX>V7y|Bq$xFBTokl#^;wAOWQ_5M*bV2>WD+@rD^ zuLWE(R9Z~rZ7!Z`Jm@qUuEB|pxK(am_*V}1$wc0o&3_2>+kJSy#Uk+~P%qwPFiDKx z%zl~ZRNbycE&8$7x2wfrhJy+z-kk_vWM*Q5gp2okakIJRyQU$6~LFn`gl`ix88{QUy^?|45 zubn#_w}#Bl7Vd%Y7Hex&$(o(v=1PuptXE$R%`(G~el+pox0X>mMP~Y&=#uyKYiXFF zU+E3|)1Ry;RZ~TSoy1_2rKO#osQepSysUza$N?L3z_{OV>ODZVf{}Qy-SJSG@_o5C z+s7xGU0!=MAuTP2AxQtv3D_Te2>Y4qFe*<#H2cd`;|xtkibiy4*dR2)u?W1l8%7Ol z{BFbOib({^`c>G^wDFFnY%ei`kiX4B<{b_9E@=aG+b~lPp75{eZt2>!MCj$frs)eH zNFmdYlDVD(E_iLt5hN?S`70lPk3{DY5tdv3SvskUno{_;xHdv^>$W#X*9)1&Uult% zlsKM9KHQOsM&zfzN1T7sTJHu%AAhNZDi3WAG-H||Qpgjy4FU5sSjykU#xm+yRD4j)U|dZz zN~m#skc@#IS5WwG0EyQ0N!a1Mx5A@ZJXc^LVRLiWP?~bno z%#iIn?y?kquizzSaa0Sn*KIrTf`*^6@~iLPFGNc`z4Ht}2pphYj*pKEDs^Svk@)`Y z8wdU~NCojo(oc!l8;VX30U$!;msK*ubU{c6rZ=d02O?gp$yXWOF0FIt3kq$*)ma!S zxeU?E-6z^VTU(PM&KA#eBSxWU#DgDf?QnqC$(O<%U=2-RQ3ymbL9q2Do7|t^Pp;@i zhL{NY<>cfD%aJw_%H68Vt?1@2&K#(d6Xd*IXe(?jcP}Q@j6TVfxy&n} zZCkvP(K*23o0)N_cM{8{q}!7H$xYl)yxmiPSK6`ro~^4?6~L($wZR5dVQ zfIJH5Ly${`0S^j-TCfF!cnMI_>OzO#Gpam+yey>b!=676XuhX(`!)?)G=9QgCkzx4nLp#B*RbRN)%A4K5;Ov;{) zL;Fe9$|_|s4(>N7_14_~;dGdrX&m4AmYhY>evlk-exW*P7O$C_1SPWtls|G6Qa5K+ z{LdAbd_MxX`Z`eT!Y=R`MgjoYf2QvKp{PTd!b<%EuP#4VEfa<&oEr)lIn~(nfbotMF!Lp@GOfXiyWDp5>eZKqC z@g^_4R<E^*|v@5mJZyZU%x{y6>S>1Bo1M_|{<}iHD-`4X~ehOM*`)5vGdKusE72S_hALPaG`~fmR5x|<{d1g5$Dp9GvfWi-qd7WuenaPpBtCW z|2H+Jllrz5(h4uf%g%rDAEG?BW1iI1RGCj63w)D+dszqmiaEI3@CtWS0WLALY!e+u zD;Xg7($m`uHoHHp)o7W|ra&pm3imcV-TiRA3H@7%rKL>ZG^01?)vJVHlcQ9HrI89| z*zXBWfdNbq*&GxRaR#Y|dX_K{gMQ{M30%A=1}KmO6lt1(YcYHEc;VEpP2nUkdY~%& z_})6yZ28d92j*b@hQ`LyOAt3?k@Glr1Ei*jfm7nrs~JR%2K3@u@6@_NF`Vuz-qF zyaQ#KEvy}p0U^?0U$jbW{u}Dk(2*ca?#AM6XhIt;cOi2`gQUUv3Fn7I`dKdeF4MDs zb4N=HHbE1Rkh^~W4ud`11av2x0P+479%R^l`Hnifm=WX2TL_j*s8@#Lgj{CUPm{-M ziey5$`%QE-De@nXQwcbhU`2n38@g)C+QJg91M7>xg(N>`clw}@&J&Wp3X8eacLs!TQBpBKg&!iffmoj1|12 z8p&vQMA^WUp$pr+i=hsH>M0}i=d%gi$`cdK6(M1wgM`3F#1d%MXps}T=6z@!`UTbnDx%uM?D<)IQ9Xb{FzIj!5Be-``y^E>b$ z@RV>t-!>F@mQ(vV{V1n71#*l4bb?68(d~RzhmVQ5$kS^P@6?rTyu8X4*-2zMIWbU# z-=d|8ceN#~&)oPi_NumJM^2h^MRWM`0nc18Y>1yjMz$PgRerrBKhO zlRuk;6$8XP8UFe9dz^Cn)x*scS$z(s!=2z$kcXdqDRGJOEok;{U;&ya4#NVVhV(R> zGRUtT@lt^xDzaShfisEv02AUmZwrSCz(0`F0^-w4D8GUgK?qVm++1p!npCh(n}kWS zP@w>VJON8kvXMlHKDGk=xf#wV;`wO%JDYShv^r9YzI&l9(`fP#$%LG&!xe?cuAx5A*J$=O@)nlNevzZF+mE8yk zjxYaslO+)_`t@X+Rb7D?W2gSvhP$~2Hy{;r@r;@8@_0J{H0tjE;3ElCEHn@}09s!Z z=HF~*QSx$lAn&+z*nv1L^4P^i6fWc4AA^U()MEgvEhl%~JTvoab4T`3VArRDxHqZL z*MJlVT75#f9S8~scwbyBDFt=vc957oyYoy#51K@wjl_zwOW6K26%Y{;DWGUwerRnb zeZJc;YfI&Td1!#tE>B55% z;A9X&Hm3;{NP#Pj2DxDoTyEU>94=T3pizJ?SZ~hvk^B8&1jTEZ^+WxDL++ zONP_e%xdQUyuB+n;Pul2)PQ4BWrhDP5A~04BiUEkZi(MWNN*I;zJrL+8~^Jx3%*3A zr#cL+h(kDu=Oh=(Z)jDeSB>2}2=vQ*gFEoNzln$-_%XON(PltoXJbW)$yozaw59MG(#d59l+*e6=9qo*?HL z1!COKaYs=N=wPGzB|0y_sX(zHX{w>%2zUT{LEw%vltPEWX-HF}{X}L_F9GY5dt8ud)^2*MRtNZ3TDnFW4pGpTEP$j5(S#Z#*bO&$(7<6^l zQ;-7>6U71#q5`i4W##8^u2E|5;JEsaTK<%jlwVc8Czp^PO)nTN*F!!1s*kEzRzyyz zR~1G4uC2Hl8u;ssj3)9XA;HcKRlQE9-i!+Y=jby;5$E6;Q&7doF}zd-vMTWDnuR*v2>|BBq&%X@9#4D z8=XO1U_hd>b0A%vc!GF>f{K_O`Y=z~EtWgi{4(Rg zC{+cyTA|up0n7))r4-sulcMok=13yw99s^L9v#vnAatpLvlekxip>s#hu$?>ek_3O zBBq)dJ&S~L{KxNzXum7wS=K~DuZCEy=FGiF6~a2hF$_kT#{cAMKdXcbd+j>9td0y@ zrJTdV%I?WP3cY5Y+WMm)=eCT0lpv5+#-A=HK zEJ5^en|iB;7O=HUL#`t*j`cHR7@+n5gg$eQY76aO08ebXI_J^R+y+(a2lRr**dPVI z6~ayZK@pKtg)BVsR%^21GPD5-u^bt(8smlb9yC8(IeE6Ex`)p{pEx7KN);Xb9JV?n zNCG?~%*o@~ybEVgu#k%Fi45ok`&2({3JFM%r^(&cS-=Ih7c24|E8M6^S$_PQj3DN6 z8GH2=q@w-ro=DbW!!6MmZbruRb zr29LouYt%u3Cmd{fG=qGz%90bokb5oH0}s>oz8hhR=uj1VX*$K`QGR0*63Eqsr#$R zhc^Vk8x{I_-@ZSG@(AR(KQ$8Y^s6R!r${`4joeH&_-7$tk*TAV4mc-dG+xBAQ5@Nm zc8$dd^8$w*4T8Gj zcDRuS9MR3wH|pIik)Sgk(j5u9l;6MCT2MzYe!^m^jiX8m3&n?aq9>e}^&w{-I{3)G zC^t8WY@*vWN{BrOc8xHF!d6n z0~dr*3T)2Kfqw%Tb@NNH#(MJ5r?^Cpju+5++1R}gUit4rJ7zEoh!hxzd2W4t0q*b% zXilOPnIOXIFlw$YssNsc^#X7h$!aW!eN>1wgwcC0cghCB-DLrzAj(BugBte)w3ur8 z2@$kF27Ma9dk4#V?nHp3U(bI93{uZ5vFOd)tFw7&;h!UvL)i>dWoyi0Y69R6mW3mP-p0m0x~A&mXmSe4vBd05^XMylIe$^K=1Y9p|!%+(uYB z0}sWNyMEq*!@lmm3|AVHNvrgdsi)5D!x?M)3*NGoB))v#>uR*b+C(e`^~aEgc<1Zf zUeuh+vEAd2bCj|--svK9!)2~@mEH#(+&0Sr0Dw|$PZ}Hv={#4wWv9RRG2yg1^$t0r zo%!D{5F5&215YL(M1+8A z#k7mnkx?M_gMMckBK+VL?g+(G8#^`7q9qP!Nxy;W*3{HJI4?&+Na3G$FJal&-2ix2 zfdMlKPdYdc`EG(d*DF@%Px>12Lx8oN%hh(Z5$D00#VqP)G;N1kFuKA}-C` z(Z3u9^HN6P>V+rK@U6%DUY{XD&w4`IpgiM!{qL8->wCDM zrmCv-i9!m$m|L^$*_wnONsq~xn2DigcCiRuAX~YVxT4 zfK!6i(93g#&sL^MZmo)$g}?B~2Y8~E^a2K5Y{`hLTN~sHZUFi5_Tu6q+PRnjIkX-+ z?CXOM@a6BTe#FDR@G4w_4^AntZ-cMA4qgj)l*ix*h9+<>g{`|d;M#`1P~`%fUvBP| z%u2P~&JY(Ik~B1$Dw`{Sqf2;*t6A|H^`em$uJO*b%~`t~ba|=FF?5e?CWhYm0UdUo zaZVx2ujH{@Xv*ZTy>XS&s%ao75+JCm1sC@VR~H5L+9~nGv^Nw0wi}}3F|8WU@OeHa zz76qP<4%dqt%H#Kz#kARIDLj1WUMX_A>vVf6pg87xJju?65J#M!yjN1d6t#+re6Ac zc)y}4s^eFn#KYDKc7#bFtifR{P7QVg+)qF@=zzK*Km5@8 z_4IPT5|fyX=4TtwCcuw>-JfNm0f&Knfu|+P2hJ!WTHcj_+V%%o_e&M~3+ef=pOSW4 zmt~{ls+vfuYvd2e9JbBmvb}B6y>F_@V;HnYu_9?AZ6d<`Vyrpjx@C9?@~Y zntYLa`Iv;w?CEop?*&bxk|OIK`U4~X2C4LF4kd8n-G?w%4(&Yf`ZSV5buv8R{*1wR(m zqu@;jiW*cq;JpJ!v`NF>Uj1*kF3MY*f>*;sD2<|ejQs9cU;%CxIZBwl=T)Yl{OYG) zjBJulz`vmW#o6-r2Q>s``rmNjiDb1HXUaeJgI0oDRM-S_wLPlZ|F_eU0f-2VK1Gkx z;?M`sYg_=%$$bV$Rfz%cu$>7xg^`_k8P|MoL+ke88R*FZ7^q|>m*m9`DPkXdA~I1@ zN>Xc;T-R88`b|i0ZlTvbbYJNJXEB*L2s3z@tar$WIuln)VvaPkJ6E-%YRta@C;IRn zs616Fb2SJ8Ri_uBj@ZM~e$PQg>Vm0r{;xw-3e@0W;+o)+r&`wUF6K8=`?6c|`Q$59 z*`T}*hd_RzuFizBchBAs{~7j(`WOWx+)Xg@j_`sSRCtk*i%!=2C8g{1J*FQm zburBqEF<^fXR~8=u}a#qbQ3@8w89)){I#r`7o+`=cz^8PM#}f^p8yj4{?VU&G6YvY zJ{zS#Y`zNw=@w1_E8h~(k|N=_%_oCE6cP0AV&7yFv(eFkh=wf4<}k~%(uEWAL^c=P z`Ej=%Vodf$8XqPzbpXi)gE|+GRbaCTDx)*E@~`k!%p*W*??(aaO9x}_TdI%|XGR!9 zl=OSnVe=@HUPwJUv_#Omie`xU6=k&(LW(>JS$kSV5fIoOBrF~?Hc#_)Rn4I_h2mCV z5ZiD4MH%epSID2`Q@>@|+KLo0qz!GYtye+lUM%6sQjD@PRU2xgs=HWU?G|^Zja#8s z=&A$B+%I)IYCbn7w_6hiVVB*GI0Wnmarpo6ehgUdEJlk^`=A`Zgy5V2)&PSWWtg){ z0>UJwVM($ZQ^RaedDqO-g<#aG2V-JdF=(XO-(cvWQJe)QHk|k~`x36d;(A!H2Ma!A zpA#X5P5wv~oE<@)T^g%EG~rX@NaM%L@d6``tzEkqbfFaess55vB-NzS3An4r@#^eYNj|Z~3mNIcxGP%6?99y<$k$C4t>NTyL|sKwIZMer;D0Ukj}ZU> literal 0 HcmV?d00001 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