diff --git a/README.md b/README.md
index 2da7b9cf..555118a6 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# pydraughts
[![PyPI version](https://badge.fury.io/py/pydraughts.svg)](https://badge.fury.io/py/pydraughts) [![Tests](https://github.com/AttackingOrDefending/pydraughts/actions/workflows/tests.yml/badge.svg)](https://github.com/AttackingOrDefending/pydraughts/actions/workflows/tests.yml) [![Build](https://github.com/AttackingOrDefending/pydraughts/actions/workflows/build.yml/badge.svg)](https://github.com/AttackingOrDefending/pydraughts/actions/workflows/build.yml) [![CodeQL](https://github.com/AttackingOrDefending/pydraughts/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/AttackingOrDefending/pydraughts/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/AttackingOrDefending/pydraughts/branch/main/graph/badge.svg?token=ZSPXIVSAWN)](https://codecov.io/gh/AttackingOrDefending/pydraughts)
-pydraughts is a draughts (checkers) library for Python with move generation, PDN reading and writing, engine communication and balloted openings. It is based on [ImparaAI/checkers](https://github.com/ImparaAI/checkers).
+pydraughts is a draughts (checkers) library for Python with move generation, SVG visualizations, PDN reading and writing, engine communication and balloted openings. It is based on [ImparaAI/checkers](https://github.com/ImparaAI/checkers).
Installing
----------
@@ -48,7 +48,13 @@ board.push(move)
board2 = Board(fen="W:WK40:B19,29")
board2.push(Move(board2, pdn_move='40x14'))
```
-* Get a visual representation of the board
+* Get a visual representation of the board as SVG
+```python
+from draughts import svg
+svg.create_svg(Board(fen="B:W16,19,33,34,47,K4:B17,25,26"))
+```
+![SVG Board](examples/board.svg)
+* Get a visual representation of the board in the terminal
```python
print(board)
diff --git a/draughts/__init__.py b/draughts/__init__.py
index 5387952c..504a4266 100644
--- a/draughts/__init__.py
+++ b/draughts/__init__.py
@@ -3,5 +3,5 @@
__all__ = ["Board", "Move", "WHITE", "BLACK"]
__author__ = "Ioannis Pantidis"
-__copyright__ = "2021-2024, " + __author__
-__version__ = "0.6.6"
+__copyright__ = "2021-2025, " + __author__
+__version__ = "0.6.7"
diff --git a/draughts/svg.py b/draughts/svg.py
new file mode 100644
index 00000000..64816d85
--- /dev/null
+++ b/draughts/svg.py
@@ -0,0 +1,94 @@
+import math
+from draughts import Board
+
+
+def create_svg(board: Board) -> str:
+ """Create an SVG of a board."""
+ # Base square size
+ square_size = 40
+ margin = 16 # Fixed margin size for coordinates
+
+ # Calculate SVG dimensions based on board size
+ str_representation = list(map(lambda row_str: row_str.split("|"), filter(lambda row_str: "|" in row_str, str(board).split("\n"))))
+ width = len(str_representation[0])
+ height = len(str_representation)
+ svg_width = (square_size * width) + (2 * margin)
+ svg_height = (square_size * height) + (2 * margin)
+
+ # Background color for coordinates
+ svg = [f'''')
+ return '\n'.join(svg)
diff --git a/examples/board.svg b/examples/board.svg
new file mode 100644
index 00000000..b98acd88
--- /dev/null
+++ b/examples/board.svg
@@ -0,0 +1,140 @@
+
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index a3f79368..fa22d0b0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,7 +2,7 @@
name = pydraughts
version = attr: draughts.__version__
author = Ioannis Pantidis
-description = A draughts library for Python with move generation, PDN reading and writing, engine communication and balloted openings
+description = A draughts library for Python with move generation, SVG visualizations, PDN reading and writing, engine communication and balloted openings
long_description = file: README.md
long_description_content_type = text/markdown
keywords = checkers, draughts, game, fen, pdn
diff --git a/test_pydraughts/test_engines.py b/test_pydraughts/test_engines.py
index 291ff3b8..5ac0270c 100644
--- a/test_pydraughts/test_engines.py
+++ b/test_pydraughts/test_engines.py
@@ -211,6 +211,7 @@ def test_checkerboard_engines():
break
logger.info('Finished playing 1')
checkerboard.kill_process()
+ logger.info('Closed engines 1')
checkerboard = CheckerBoardEngine('cake_189f.dll')
limit = Limit(10)
@@ -225,6 +226,7 @@ def test_checkerboard_engines():
break
logger.info('Finished playing 2')
checkerboard.kill_process()
+ logger.info('Closed engines 2')
@pytest.mark.timeout(300, method="thread")
@@ -245,6 +247,7 @@ def test_russian_checkerboard_engines():
break
logger.info('Finished playing 1')
checkerboard.kill_process()
+ logger.info('Closed engines 1')
checkerboard = CheckerBoardEngine('kestog.dll')
limit = Limit(10)
@@ -259,6 +262,7 @@ def test_russian_checkerboard_engines():
break
logger.info('Finished playing 2')
checkerboard.kill_process()
+ logger.info('Closed engines 2')
@pytest.mark.timeout(450, method="thread")
diff --git a/test_pydraughts/test_pdn.py b/test_pydraughts/test_pdn.py
index 40c4170a..2e703bf2 100644
--- a/test_pydraughts/test_pdn.py
+++ b/test_pydraughts/test_pdn.py
@@ -12,7 +12,7 @@
def download_games():
headers = {'User-Agent': 'User Agent', 'From': 'mail@mail.com'}
- response = requests.get('https://pdn.fmjd.org/_downloads/games.zip', headers=headers, allow_redirects=True)
+ response = requests.get('https://github.com/wiegerw/pdn/raw/refs/heads/master/games/games.zip', headers=headers, allow_redirects=True)
with open('./TEMP/games.zip', 'wb') as file:
file.write(response.content)
with zipfile.ZipFile('./TEMP/games.zip', 'r') as zip_ref:
diff --git a/test_pydraughts/test_svg.py b/test_pydraughts/test_svg.py
new file mode 100644
index 00000000..36232128
--- /dev/null
+++ b/test_pydraughts/test_svg.py
@@ -0,0 +1,148 @@
+from draughts import Board, Move
+from draughts.svg import create_svg
+
+
+def test_svg():
+ board = Board(fen="W:WK4,19,27,33,34,47:B11,12,17,22,25,26:H0:F50")
+ board.push(Move(board, pdn_move="27x16"))
+ svg = create_svg(board)
+ assert svg == """"""