Skip to content

Commit

Permalink
Merge pull request #75 from Hacking3DPrinters/development
Browse files Browse the repository at this point in the history
Merge update 1.0b0
  • Loading branch information
HippoProgrammer authored Jun 30, 2024
2 parents 11c39e7 + eb9d48e commit f8b2d5c
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 103 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For documentation, see [our wiki](https://github.com/Hacking3DPrinters/robotic-c

### 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
* 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
Expand Down Expand Up @@ -66,4 +66,4 @@ You can also submit a 'feature request' issue: give us some example code if you

## Status

This project is in ACTIVE DEVELOPMENT.
This project is in ACTIVE DEVELOPMENT.
Binary file removed dist/robotic_chess-1.0a1-py3-none-any.whl
Binary file not shown.
Binary file removed dist/robotic_chess-1.0a1.tar.gz
Binary file not shown.
Binary file added dist/robotic_chess-1.0b0-py3-none-any.whl
Binary file not shown.
Binary file added dist/robotic_chess-1.0b0.tar.gz
Binary file not shown.
41 changes: 41 additions & 0 deletions robotic_chess.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: package robotic_chess</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">

<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>robotic_chess</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/codespace/.python/current/lib/python3.10/site-packages/robotic_chess/__init__.py">/home/codespace/.python/current/lib/python3.10/site-packages/robotic_chess/__init__.py</a></font></td></tr></table>
<p></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>

<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="robotic_chess.chess.html">chess</a><br>
</td><td width="25%" valign=top><a href="robotic_chess.gcode.html">gcode</a><br>
</td><td width="25%" valign=top><a href="robotic_chess.octoprint.html">octoprint</a><br>
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>

<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-human_move"><strong>human_move</strong></a>()</dt></dl>
<dl><dt><a name="-letter_to_number"><strong>letter_to_number</strong></a>(letter)</dt></dl>
<dl><dt><a name="-notation_to_coords"><strong>notation_to_coords</strong></a>(move='a1a2')</dt></dl>
<dl><dt><a name="-robot_move"><strong>robot_move</strong></a>(best_move)</dt></dl>
<dl><dt><a name="-rounddown"><strong>rounddown</strong></a> = floor(x, /)</dt><dd><tt>Return&nbsp;the&nbsp;floor&nbsp;of&nbsp;x&nbsp;as&nbsp;an&nbsp;Integral.<br>
&nbsp;<br>
This&nbsp;is&nbsp;the&nbsp;largest&nbsp;integer&nbsp;&lt;=&nbsp;x.</tt></dd></dl>
<dl><dt><a name="-roundup"><strong>roundup</strong></a> = ceil(x, /)</dt><dd><tt>Return&nbsp;the&nbsp;ceiling&nbsp;of&nbsp;x&nbsp;as&nbsp;an&nbsp;Integral.<br>
&nbsp;<br>
This&nbsp;is&nbsp;the&nbsp;smallest&nbsp;integer&nbsp;&gt;=&nbsp;x.</tt></dd></dl>
</td></tr></table>
</body></html>
21 changes: 15 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from setuptools import setup
from os import system
import platform
import getpass

if __name__ == "__main__":
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')

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')
elif platform.system()=='Windows':
setup()
system('git clone https://github.com/official-stockfish/Stockfish.git')
system('cd Stockfish/src/ && make -j build')
system('move Stockfish/src/stockfish C:/Users/'+str(getpass.getuser())+'/stockfish')
else:
raise OSError('OS unsupported.')
64 changes: 53 additions & 11 deletions src/robotic_chess.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: robotic_chess
Version: 1.0a1
Version: 1.0b0
Summary: A 3D printer-based chess playing robot.
Author-email: Benjamin Porter <bcgcustomerservices@gmail.com>
Project-URL: Homepage, https://github.com/hippoprogrammer/robotic-chess
Expand All @@ -13,32 +13,74 @@ Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: stockfish==3.28.0
Requires-Dist: octoprint-cli==3.3.2
Requires-Dist: chess==1.10.0

<p align="center">
<img src="https://raw.githubusercontent.com/Hacking3DPrinters/robotic-chess/main/3DCHESS.png", width=20%, height=auto />
</p>


# 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 (recommended)

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.
### Installation from source (for development)

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).

---

## Running

THIS DOES NOT INSTALL STOCKFISH OR OCTOPRINT (will be included in the future).
### Running from wheel (recommended)

### Installation from source
Run `python3 -m robotic_chess`.

### Running from source (for development)

If installed from .whl: Follow instructions above.

Otherwise, run `cd {CLONED_DIR}/src/robotic_chess` (where {CLONED_DIR} is the directory you cloned into) and `python3 __init__.py`.

---

## 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.
5 changes: 3 additions & 2 deletions src/robotic_chess.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
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
Expand Down
1 change: 1 addition & 0 deletions src/robotic_chess.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
stockfish==3.28.0
octoprint-cli==3.3.2
chess==1.10.0
104 changes: 47 additions & 57 deletions src/robotic_chess/__init__.py → src/robotic_chess/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import robotic_chess.octoprint # import printer lib

from robotic_chess.gcode import Parser # import gcode lib
from robotic_chess.chess import Board # import chess lib
from robotic_chess.engine import Engine # import chess lib

from multiprocessing import cpu_count as cpus
from math import ceil as roundup
Expand Down Expand Up @@ -40,8 +40,7 @@ def human_move(): # take move from human
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 computer_move(board): # take move from engine
best_move = board.engine_move() # get best move
def robot_move(best_move): # make move
best_move_coords = notation_to_coords(move=best_move) # get coords for best move
if 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
Expand All @@ -57,67 +56,58 @@ def computer_move(board): # take move from engine
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
return best_move

print('Robotic Chess v1.0.0')
print('MIT Licence 2024 Benjamin Porter and Zachary Birket')
print()

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
if mode==2 and cpus()<4:
print('Not enough CPU cores. Please retry.')
if __name__ == "__main__":
if cpus()<3:
print('Not enough CPUs.')
sys.exit()
else:
print('Mode invalid. Please retry.') # otherwise exit
sys.exit()

if mode==0 or mode==1: # if <=1 computers are playing
b = Board(cpu=round(roundup((cpus()-2))) # initialise classes

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: ')))
else: # if 2 computers are playing
b1 = Board(cpu=round(roundup((cpus()-2)/2))) # set up two computer instances
b2 = Board(cpu=round(rounddown((cpus()-2)/2)))
p = Parser()
printer = robotic_chess.octoprint.Printer()
print('Please select a rating for bot 1:')
print('Valid ratings are between 100 and 3100')
b1.engine_skill(int(input('Rating: ')))
print('Please select a rating for bot 2:')
b2.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
b.opponent_move(human_move()) # take move
elif mode==1: # if human vs human
print(human_move) # display human move to remote human
else: # if computer vs computer
b2.opponent_move(computer_move(b1)) # take move and record for opponent
if mode==0: # if human vs computer
computer_move(b)
elif mode==1: # if human vs human
# take move from remote human
pass
else:
b1.opponent_move(computer_move(b2)) # take move and record for opponent
# check for win after each move

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
b.opponent_move(human_move()) # take move
elif mode==1: # if human vs human
print(human_move) # display human move to remote human
else: # if computer vs computer
computer_move(b.engine_move()) # take move and record for opponent
if b.check_win():
break
if mode==0: # if human vs computer
computer_move(b.engine_move())
elif mode==1: # if human vs human
# take move from remote human
pass
else:
computer_move(b.engine_move()) # take move and record for opponent
# check for win after each move
if b.check_win():
break

25 changes: 0 additions & 25 deletions src/robotic_chess/chess.py

This file was deleted.

44 changes: 44 additions & 0 deletions src/robotic_chess/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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"
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)
return move
def opponent_move(self,move):
# expects an UCI string
self.board.push_uci(move)
self.stockfish.make_moves_from_current_position([move.uci()])
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')

0 comments on commit f8b2d5c

Please sign in to comment.