diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index bf0b17e..a5eafc3 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -29,6 +29,6 @@ jobs: python -m pip install --upgrade pip python -m pip install --upgrade pytest python -m pip install -r requirements.txt - - name: Test with pytest + - name: Test with custom test suite run: | python tests.py diff --git a/README.md b/README.md index 50b8697..d8c31f0 100644 --- a/README.md +++ b/README.md @@ -5,29 +5,62 @@ # Robotic Chess -An chess playing robot, powered by Stockfish. +An chess playing robot, powered by Stockfish and Octoprint. For documentation, see [our wiki](https://github.com/Hacking3DPrinters/robotic-chess/wiki). --- +## Requirements + +### Essentials +* 3-core CPU and at least 3GB of RAM (theoretically this can run with only 2 cores and 2GB of RAM, but at the risk of crashing / hanging your computer) +* A Linux-based operating system with sudo OR a Windows 10/11 operating system. +* Python 3.10+ and pip +* Octoprint server connected to a 3D printer +* An electromagnet connected to M106/M107 extruder fan control + +### Recommended +* 4-core+ CPU and at least 6GB of RAM +* Python 3.11+ and pip +* Knowledge of simple Linux commands + +--- + ## Installation -### Installation from wheel +### Installation from wheel (beta for Linux) -First, visit [our releases page](https://github.com/Hacking3DPrinters/robotic-chess/releases) and download the `.whl` and `requirements.txt` files from the desired version. +First, visit [our releases page](https://github.com/Hacking3DPrinters/robotic-chess/releases) and download the `.whl` file from the desired version (the 'latest' version is recommended). Then, run ```pip install robotic_chess-0.2.1-py3.whl``` (replace 0.2.1 with the version number of your downloaded wheel). -Finally, run -```pip install -r requirements.txt``` -to install dependencies. +You must then edit `~/.config/octoprint-cli.ini` and input the server details of your octoprint server. -THIS DOES NOT INSTALL STOCKFISH OR OCTOPRINT (will be included in the future). +### Installation from source (recommended for Windows or Linux dev builds) + +First, clone our repo using `git clone https://github.com/Hacking3DPrinters/robotic-chess.git`, and enter the new directory. +Then edit `config.ini` with your server details of the octoprint server. + +Then run `python3 setup.py install` to begin setup. + +--- -### Installation from source +## Running + +Run `python3 -m robotic_chess`. + +--- + +## Contributing + +Please feel free to fork our repo then submit a pull request: we'd love it if you would help us develop new features! + +You can also submit a 'feature request' issue: give us some example code if you feel like contributing personally! + +--- -First, clone our repo using `git clone https://github.com/Hacking3DPrinters/robotic-chess.git`, and enter the new directory. Then do `pip install dist/robotic_chess-0.2.1-py3-none-any.whl` (replace 0.2.1 with the desired version number), followed by `pip install -r requirements.txt`. +## Status -THIS DOES NOT INSTALL STOCKFISH OR OCTOPRINT (will be included in the future). +This project is in ACTIVE DEVELOPMENT. diff --git a/SECURITY.md b/SECURITY.md index c66d97b..8114a5d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,10 +4,11 @@ | Version | Supported | | ------- | ------------------ | -| >0.2.1 | ✅ Fully Supported (latest) | -| 0.2.0 | ✳️ Fully Supported | -| 0.1 | :x: Not Supported | -| 0.1a | :x: Not Supported | +| 1.0.0 | ✅ Fully Supported (latest) | +| 1.0a/b/rc | ✳️ Partially Supported | +| 0.2.x | :x: Not Supported | +| 0.1 | :x: Not Supported | +| 0.1a | :x: Not Supported | ## Reporting a Vulnerability diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..e0450f4 --- /dev/null +++ b/config.ini @@ -0,0 +1,15 @@ +[server] +;Set OctoPrint server address and x-api-key +ServerAddress = SERVER_ADDRESS_HERE +ApiKey = API_KEY_HERE + +[preferences] +;Set if the program uses colored or formatted text, this setting is turned off on windows due to cmd and powershell limitations +FormattedText = true +;Set if the program should check for updates +UpdateCheck = true + +[printer] +;Set maximum temperature that printer can be set to +MaxExtruderTemp = 250 +MaxBedTemp = 85 diff --git a/dist/robotic_chess-1.0.0-py3-none-any.whl b/dist/robotic_chess-1.0.0-py3-none-any.whl new file mode 100644 index 0000000..cb74051 Binary files /dev/null and b/dist/robotic_chess-1.0.0-py3-none-any.whl differ diff --git a/dist/robotic_chess-1.0.0.tar.gz b/dist/robotic_chess-1.0.0.tar.gz new file mode 100644 index 0000000..d473ebb Binary files /dev/null and b/dist/robotic_chess-1.0.0.tar.gz differ diff --git a/dist/robotic_chess-1.0b0-py3-none-any.whl b/dist/robotic_chess-1.0b0-py3-none-any.whl new file mode 100644 index 0000000..1e58996 Binary files /dev/null and b/dist/robotic_chess-1.0b0-py3-none-any.whl differ diff --git a/dist/robotic_chess-1.0b0.tar.gz b/dist/robotic_chess-1.0b0.tar.gz new file mode 100644 index 0000000..8754b55 Binary files /dev/null and b/dist/robotic_chess-1.0b0.tar.gz differ diff --git a/dist/robotic_chess-1.0rc1-py3-none-any.whl b/dist/robotic_chess-1.0rc1-py3-none-any.whl new file mode 100644 index 0000000..ae5b4af Binary files /dev/null and b/dist/robotic_chess-1.0rc1-py3-none-any.whl differ diff --git a/dist/robotic_chess-1.0rc1.tar.gz b/dist/robotic_chess-1.0rc1.tar.gz new file mode 100644 index 0000000..f07cf40 Binary files /dev/null and b/dist/robotic_chess-1.0rc1.tar.gz differ diff --git a/example.gcode b/example.gcode deleted file mode 100644 index 2f5ea7a..0000000 --- a/example.gcode +++ /dev/null @@ -1,8 +0,0 @@ -; we recommend running this file first to check your 3D printer is compatible -G21 ; -G90 ; -G28 ; printer should move to home -G00 X20 Y20 Z20 -G01 X0 Y0 Z0 F100 -M106 -M107 diff --git a/pyproject.toml b/pyproject.toml index 2780c76..438e826 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "robotic_chess" -version = "0.2.4" +version = "1.0.2" authors = [ { name="Benjamin Porter", email="bcgcustomerservices@gmail.com" }, ] @@ -16,7 +16,12 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] +dependencies = [ + "stockfish@git+https://github.com/hacking3dprinters/stockfish-python.git", + "octoprint-cli==3.3.2", + "chess==1.10.0" +] [project.urls] -Homepage = "https://github.com/hippoprogrammer/robotic-chess" -Issues = "https://github.com/hippoprogrammer/robotic-chess/issues" +Homepage = "https://github.com/hacking3dprinters/robotic-chess" +Issues = "https://github.com/hacking3dprinters/robotic-chess/issues" diff --git a/requirements.txt b/requirements.txt index c38aaf2..94d3b90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ # pip requirements file for robotic-chess -stockfish==3.28.0 +git+https://github.com/hacking3dprinters/stockfish-python.git#egg=stockfish octoprint-cli==3.3.2 chess==1.10.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e1edb2d --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup +from os import system +import platform +import getpass + +if __name__ == "__main__": + if platform.system()=='Linux': + setup() + system('git clone https://github.com/official-stockfish/Stockfish.git') + system('cd Stockfish/src/ && make -j build') + system('sudo mv Stockfish/src/stockfish /usr/local/bin/') + system('sudo chmod a+x /usr/local/bin/stockfish') + system('mv config.ini ~/.config/octoprint-cli.ini') + elif platform.system()=='Windows': + setup() + system('tar -xf stockfish-windows.zip') + system('mkdir C:/Users/'+getpass.getuser()+'/stockfish/') + system('move stockfish/stockfish-windows-x86-64-sse41-popcnt.exe C:/Users/'+getpass.getuser()+'/stockfish/stockfish.exe') + # config.ini does not need to be moved + else: + raise OSError('OS unsupported.') diff --git a/src/robotic_chess.egg-info/PKG-INFO b/src/robotic_chess.egg-info/PKG-INFO index cff2252..c87552c 100644 --- a/src/robotic_chess.egg-info/PKG-INFO +++ b/src/robotic_chess.egg-info/PKG-INFO @@ -1,42 +1,83 @@ Metadata-Version: 2.1 Name: robotic_chess -Version: 0.2.4 +Version: 1.0.0 Summary: A 3D printer-based chess playing robot. Author-email: Benjamin Porter -Project-URL: Homepage, https://github.com/hippoprogrammer/robotic-chess -Project-URL: Issues, https://github.com/hippoprogrammer/robotic-chess/issues +Project-URL: Homepage, https://github.com/hacking3dprinters/robotic-chess +Project-URL: Issues, https://github.com/hacking3dprinters/robotic-chess/issues Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Requires-Python: >=3.10 Description-Content-Type: text/markdown License-File: LICENSE +Requires-Dist: stockfish@ git+https://github.com/hacking3dprinters/stockfish-python.git +Requires-Dist: octoprint-cli==3.3.2 +Requires-Dist: chess==1.10.0 + +

+ +

+ # Robotic Chess -An chess playing robot, powered by Stockfish. +An chess playing robot, powered by Stockfish and Octoprint. For documentation, see [our wiki](https://github.com/Hacking3DPrinters/robotic-chess/wiki). --- +## Requirements + +### Essentials +* 3-core CPU and at least 3GB of RAM (theoretically this can run with only 2 cores and 2GB of RAM, but at the risk of crashing / hanging your computer) +* A Linux-based operating system with sudo OR a Windows 10/11 operating system. +* Python 3.10+ and pip +* Octoprint server connected to a 3D printer +* An electromagnet connected to M106/M107 extruder fan control + +### Recommended +* 4-core+ CPU and at least 6GB of RAM +* Python 3.11+ and pip +* Knowledge of simple Linux commands + +--- + ## Installation -### Installation from wheel +### Installation from wheel (beta for Linux) -First, visit [our releases page](https://github.com/Hacking3DPrinters/robotic-chess/releases) and download the `.whl` and `requirements.txt` files from the desired version. +First, visit [our releases page](https://github.com/Hacking3DPrinters/robotic-chess/releases) and download the `.whl` file from the desired version (the 'latest' version is recommended). Then, run ```pip install robotic_chess-0.2.1-py3.whl``` (replace 0.2.1 with the version number of your downloaded wheel). -Finally, run -```pip install -r requirements.txt``` -to install dependencies. +You must then edit `~/.config/octoprint-cli.ini` and input the server details of your octoprint server. + +### Installation from source (recommended for Windows or Linux dev builds) -THIS DOES NOT INSTALL STOCKFISH OR OCTOPRINT (will be included in the future). +First, clone our repo using `git clone https://github.com/Hacking3DPrinters/robotic-chess.git`, and enter the new directory. +Then edit `config.ini` with your server details of the octoprint server. -### Installation from source +Then run `python3 setup.py` to begin setup. + +--- + +## Running + +Run `python3 -m robotic_chess`. + +--- + +## Contributing + +Please feel free to fork our repo then submit a pull request: we'd love it if you would help us develop new features! + +You can also submit a 'feature request' issue: give us some example code if you feel like contributing personally! + +--- -First, clone our repo using `git clone https://github.com/Hacking3DPrinters/robotic-chess.git`, and enter the new directory. Then do `pip install dist/robotic_chess-0.2.1-py3-none-any.whl` (replace 0.2.1 with the desired version number), followed by `pip install -r requirements.txt`. +## Status -THIS DOES NOT INSTALL STOCKFISH OR OCTOPRINT (will be included in the future). +This project is in ACTIVE DEVELOPMENT. diff --git a/src/robotic_chess.egg-info/SOURCES.txt b/src/robotic_chess.egg-info/SOURCES.txt index fbf0ddc..c92b917 100644 --- a/src/robotic_chess.egg-info/SOURCES.txt +++ b/src/robotic_chess.egg-info/SOURCES.txt @@ -1,11 +1,13 @@ LICENSE README.md pyproject.toml -src/robotic_chess/__init__.py -src/robotic_chess/chess.py +setup.py +src/robotic_chess/__main__.py +src/robotic_chess/engine.py src/robotic_chess/gcode.py src/robotic_chess/octoprint.py src/robotic_chess.egg-info/PKG-INFO src/robotic_chess.egg-info/SOURCES.txt src/robotic_chess.egg-info/dependency_links.txt +src/robotic_chess.egg-info/requires.txt src/robotic_chess.egg-info/top_level.txt \ No newline at end of file diff --git a/src/robotic_chess.egg-info/requires.txt b/src/robotic_chess.egg-info/requires.txt new file mode 100644 index 0000000..ef106b8 --- /dev/null +++ b/src/robotic_chess.egg-info/requires.txt @@ -0,0 +1,3 @@ +stockfish@ git+https://github.com/hacking3dprinters/stockfish-python.git +octoprint-cli==3.3.2 +chess==1.10.0 diff --git a/src/robotic_chess/__init__.py b/src/robotic_chess/__init__.py deleted file mode 100644 index 9738b32..0000000 --- a/src/robotic_chess/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -import robotic_chess.chess -import robotic_chess.gcode -import robotic_chess.octoprint -# main code goes here diff --git a/src/robotic_chess/__main__.py b/src/robotic_chess/__main__.py new file mode 100644 index 0000000..a2d74a6 --- /dev/null +++ b/src/robotic_chess/__main__.py @@ -0,0 +1,120 @@ +print('Initialising libraries...') +import robotic_chess.octoprint # import printer lib + +from robotic_chess.gcode import Parser # import gcode lib +from robotic_chess.engine import Engine # import chess lib + +from chess import IllegalMoveError + +from multiprocessing import cpu_count as cpus +from math import ceil as roundup +from math import floor as rounddown +import sys # import exit lib +# main code goes here +print('Initialised.') + +def letter_to_number(letter): # convert chess columns (a-h) to numbers (1-8) + if letter=='a': + return 1 + elif letter=='b': + return 2 + elif letter=='c': + return 3 + elif letter=='d': + return 4 + elif letter=='e': + return 5 + elif letter=='f': + return 6 + elif letter=='g': + return 7 + elif letter=='h': + return 8 + +def notation_to_coords(move='a1a2'): # use complex function to convert a square-square move to [[coordx,coordy],[coordx,coordy]] format + return [[15.9375+((letter_to_number(move[0:1])-1)*31.875),15.9375+((int(move[1:2])-1)*31.875)],[15.9375+((letter_to_number(move[2:3])-1)*31.875),15.9375+((int(move[3:4])-1)*31.875)]] + +def human_move(): # take move from human + while True: # while move is invalid + h_move = str(input('Input move: ')) # take a move + if len(h_move)==4: # if move is valid + break # break loop + print('Input invalid. Should be formatted as [start][end], e.g. a1a2 for square a1 to square a2') # if move is invalid, print error message + return h_move # return move + +def robot_move(best_move): # make move + best_move_coords = notation_to_coords(move=best_move) # get coords for best move + if b.get_capture(best_move): # if move is a capture + capture_coords={'x':best_move_coords[1][0],'y':best_move_coords[1][1],'z':piece_height} # find coords for the capture + printer.run_gcode(p.add_movement(x=capture_coords['x'], y=capture_coords['y'], z=capture_coords['z'], speed=500)) # go to those coords + printer.run_gcode(p.add_fan()) # pick up piece + printer.run_gcode(p.add_movement(z=20, speed=1000)) # move up + printer.run_gcode(p.add_movement(x=191.25,y=255,speed=500)) + printer.run_gcode(p.add_fan(speed=0)) # release piece + printer.run_gcode(p.add_home()) # go home + printer.run_gcode(p.add_movement(x=best_move_coords[0][0], y=best_move_coords[0][1], z=piece_height, speed=500)) # find coords of start square and go there + printer.run_gcode(p.add_fan()) # pick up piece + printer.run_gcode(p.add_movement(z=20, speed=1000)) # move up + printer.run_gcode(p.add_movement(x=best_move_coords[1][0], y=best_move_coords[1][1], z=piece_height, speed=500)) # find coords of end square and go there + printer.run_gcode(p.add_fan(speed=0)) # release piece + printer.run_gcode(p.add_home()) # go home + +print('Robotic Chess v1.0.0') +print('MIT Licence 2024 Benjamin Porter and Zachary Birket') +print() +if __name__ == "__main__": + if cpus()<3: + print('Not enough CPUs.') + sys.exit() + + print('Please select a game mode:') + print('Mode 0: Physical Human vs. Virtual Computer') + print('Mode 1: Physical Human vs. Virtual Human') + print('Mode 2: Virtual Computer vs. Virtual Computer') + + mode = int(input('Mode (0/1/2): ')) # track game mode (0 = human vs. computer, 1 = human vs. human-controlled computer, 2 = computer vs. computer) - functionality will be added later + + if mode==0 or mode==1 or mode==2: # check if mode is valid + pass + else: + print('Mode invalid. Please retry.') # otherwise exit + sys.exit() + + b = Engine(cpu=round(roundup((cpus()-2)))) # initialise classes + p = Parser() + printer = robotic_chess.octoprint.Printer() + print('Please select a rating:') + print('Valid ratings are between 100 and 3100') + b.engine_skill(int(input('Rating: '))) + + piece_height = 100 # define piece height in mm + + printer.run_gcode(p.setup(rel_pos=False)) + + playing = True # track game state + + while playing: # while game is ongoing + if mode==0: # if human vs computer + while True: + try: + b.opponent_move(human_move()) # take move + break + except IllegalMoveError: + print('Illegal move, please try again') + elif mode==1: # if human vs human + print(human_move()) # display human move to remote human + else: # if computer vs computer + robot_move(b.engine_move()) # take move and record for opponent + if b.check_win(): + break + if mode==0: # if human vs computer + robot_move(b.engine_move()) + elif mode==1: # if human vs human + # take move from remote human + pass + else: + robot_move(b.engine_move()) # take move and record for opponent + # check for win after each move + if b.check_win(): + break + diff --git a/src/robotic_chess/__pycache__/chess.cpython-310.pyc b/src/robotic_chess/__pycache__/chess.cpython-310.pyc new file mode 100644 index 0000000..9e51782 Binary files /dev/null and b/src/robotic_chess/__pycache__/chess.cpython-310.pyc differ diff --git a/src/robotic_chess/__pycache__/stockfish.cpython-310.pyc b/src/robotic_chess/__pycache__/stockfish.cpython-310.pyc new file mode 100644 index 0000000..714ab8e Binary files /dev/null and b/src/robotic_chess/__pycache__/stockfish.cpython-310.pyc differ diff --git a/src/robotic_chess/chess.py b/src/robotic_chess/chess.py deleted file mode 100644 index 803c370..0000000 --- a/src/robotic_chess/chess.py +++ /dev/null @@ -1,19 +0,0 @@ -from stockfish import Stockfish -stockfish_path="" # place path to stockfish here -class Board: - def __init__(self,fen='rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',cpu=2,ram=2048): - self.stockfish = Stockfish(path=stockfish_path, parameters={"Threads": cpu, "Hash": ram}) - if self.stockfish.is_fen_valid(fen): - self.stockfish.set_fen_position(fen) - def engine_skill(self,rating=3000): - self.stockfish.set_elo_rating(rating) - def get_piece(self,square='a1'): - return self.stockfish.get_what_is_on_square(square) - def get_capture(self,move='a1a2'): - return self.stockfish.will_move_be_a_capture(move) - def engine_move(self): - return self.stockfish.get_best_move() - def opponent_move(self,moves=['a1a2']): - # multiple moves can be defined - self.stockfish.make_moves_from_current_position(moves) - \ No newline at end of file diff --git a/src/robotic_chess/engine.py b/src/robotic_chess/engine.py new file mode 100644 index 0000000..c6a95ce --- /dev/null +++ b/src/robotic_chess/engine.py @@ -0,0 +1,45 @@ +print('Loading python-chess lib...') +import chess +print('Loading stockfish lib...') +from stockfish import Stockfish +print('Loading stockfish engine...') +import getpass +import platform +if platform.system()=='Linux': + stockfish_path="/usr/local/bin/stockfish" # place path to stockfish here +elif platform.system()=='Windows': + stockfish_path="C:/Users/"+str(getpass.getuser())+"/stockfish/stockfish.exe" +else: + raise OSError('Unsupported OS') +class Engine: + def __init__(self,fenstr='rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',cpu=2,ram=2048): + self.stockfish = Stockfish(path=stockfish_path, parameters={"Threads": cpu, "Hash": ram}) + if self.stockfish.is_fen_valid(fenstr): + self.stockfish.set_fen_position(fenstr) + self.board = chess.Board(fen=fenstr) + def engine_skill(self,rating=3000): + self.stockfish.set_elo_rating(rating) + def get_piece(self,square='a1'): + return self.stockfish.get_what_is_on_square(square) + def get_capture(self,move='a1a2'): + return self.stockfish.will_move_be_a_capture(move) + def engine_move(self): + move=self.stockfish.get_best_move() + self.board.push_uci(move) + self.stockfish.make_moves_from_current_position([move]) + return move + def opponent_move(self,move): + # expects an UCI string + self.board.push_uci(move) + self.stockfish.make_moves_from_current_position([move]) + def check_win(self): + return self.board.is_game_over() + +class Board(chess.Board): + pass + +class Move(chess.Move): + pass + +print('Chesslib v2') +print('MIT Licence 2024 Benjamin Porter') diff --git a/src/robotic_chess/gcode.py b/src/robotic_chess/gcode.py index 1501120..0674cef 100644 --- a/src/robotic_chess/gcode.py +++ b/src/robotic_chess/gcode.py @@ -2,7 +2,7 @@ class Parser: def __init__(self): pass - def setup(self,rel_pos=True,mm=True,home=(0,20,0)): + def setup(self,rel_pos=True,mm=True,home=(0,0,20)): cmd_list=[] self.home=home self.pos=rel_pos @@ -29,7 +29,7 @@ def add_movement(self,x=0,y=0,z=0,speed=200): cmd+='F{f}'.format(f=str(speed)) return tuple([cmd]) else: - pass + pass def add_home(self): cmd_list=[] old_pos=self.pos @@ -54,12 +54,9 @@ def change_mm(self,mm=True): if mm: return tuple(['G21 ; mm']) else: - return tuple(['G20 ; inches']) - def change_home(self,home=(0,20,0)): - self.home=home - - - - - - + return tuple(['G20 ; inches']) + def change_home(self,home=(0,0,20)): + self.home=home + +print('Gcodelib v2.1') +print('MIT Licence 2024 Benjamin Porter') diff --git a/src/robotic_chess/octoprint.py b/src/robotic_chess/octoprint.py index 4ad8f32..56ace65 100644 --- a/src/robotic_chess/octoprint.py +++ b/src/robotic_chess/octoprint.py @@ -1,5 +1,7 @@ +print('Loading os module...') from os import system as cmd from os.path import exists +print('Loading finished.') class PrinterError(Exception): pass class Printer: @@ -20,8 +22,8 @@ def start_file(self): raise PrinterError('No file selected using Printer.load_file()') def run_gcode(self,gcode=()): if str(type(gcode))=='': - for gcmd in gcode: - cmd('octoprint-cli gcode {command}'.format(command=str(gcmd))) + for gcmd in gcode: + cmd('octoprint-cli gcode \'{command}\''.format(command=str(gcmd))) else: raise PrinterError('gcode was not in tuple form') def connect(self): @@ -29,3 +31,6 @@ def connect(self): cmd('octoprint-cli connection connect') def disconnect(self): cmd('octoprint-cli connection disconnect') + +print('Octoprintlib v1.2') +print('MIT Licence 2024 Benjamin Porter') diff --git a/stockfish-windows.zip b/stockfish-windows.zip new file mode 100644 index 0000000..de2769d Binary files /dev/null and b/stockfish-windows.zip differ