Skip to content

Commit

Permalink
Highlight key rule for Crowded House, issue #3.
Browse files Browse the repository at this point in the history
Fix font bug, and switch to Cairo for converting SVG to PNG.
  • Loading branch information
donkirkby committed Oct 29, 2023
1 parent 59161f6 commit 4916e73
Show file tree
Hide file tree
Showing 18 changed files with 126 additions and 50 deletions.
27 changes: 20 additions & 7 deletions diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def build(self) -> SvgDiagram:
square_size = 45
x0 = 15 - 0.5*square_size
y0 = 9.112 * square_size
text_elements = Drawing()
extra_elements = Drawing()
text_args = dict(text_anchor='middle',
font_family='Raleway',
font_size=round(0.55*square_size))
Expand All @@ -42,17 +42,30 @@ def build(self) -> SvgDiagram:
if command == 'text':
x = round(x0 + float(fields[1]) * square_size, 1)
y = round(y0 - float(fields[2]) * square_size, 1)
# fields[0] = str(y)
text_elements.add(text_elements.text(fields[0],
(x, y),
**text_args))
extra_elements.add(extra_elements.text(fields[0],
(x, y),
**text_args))
elif command == 'rect':
x1, y1, x2, y2 = [float(field) for field in fields]
left = round(15 + (x1-1)*square_size, 1)
top = round(15 + (8 - y2) * square_size, 1)
width = round((x2-x1+1)*square_size, 1)
height = round((y2-y1+1)*square_size, 1)
extra_elements.add(extra_elements.rect((left, top),
(width, height),
fill_opacity=0,
stroke='blue',
stroke_width=5,
stroke_dasharray='7.5'))
elif command == 'margins':
margins = tuple(float(n) for n in fields[:2])
else:
elif command == 'arrow':
tail = getattr(chess, fields[0].upper())
head = getattr(chess, fields[1].upper())
colour = fields[2]
arrows.append(chess.svg.Arrow(tail, head, color=colour))
else:
raise ValueError(f'Unknown diagram command: {command}.')
original_view_size = 390 # chess library always uses this size
view_width = original_view_size + 2*margins[0]*square_size
view_height = original_view_size + 2*margins[1]*square_size
Expand All @@ -69,7 +82,7 @@ def build(self) -> SvgDiagram:
board_tree.set('viewBox', view_box)
board_tree.set('width', str(image_width))
board_tree.set('height', str(image_height))
board_tree.extend(text_elements.get_xml())
board_tree.extend(extra_elements.get_xml())

diagram = SvgDiagram(ET.tostring(board_tree, encoding='unicode'))
return diagram
Expand Down
8 changes: 2 additions & 6 deletions diagram_differ.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ def convert_to_png(self) -> bytes:
self.write_png(png_alpha_bytes)
return png_alpha_bytes.getvalue()

def write_png(self, file: typing.Union[typing.BinaryIO, Path]):
drawing = self.diagram.to_reportlab()
png_bytes = BytesIO(drawToString(drawing, 'PNG'))
image = Image.open(png_bytes)
image_alpha = image.convert('RGBA')
image_alpha.save(file, 'PNG')
def write_png(self, file: typing.BinaryIO | Path | str):
self.diagram.to_cairo(file)

def save(self, file_path: Path) -> Path:
""" Save the image to a file.
Expand Down
Binary file modified docs/images/new_rules/diagram1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/new_rules/diagram2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/new_rules/diagram3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/new_rules/diagram4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/rules/diagram1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/rules/diagram2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/rules/diagram3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/rules/diagram4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/rules/diagram5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/rules/diagram6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 12 additions & 9 deletions docs/new_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ play order would be Walter, Betty, Winnie, Bob, Walter, Betty, and so on.
![Diagram](images/new_rules/diagram1.png)

### Rule changes
The key rule is that you may only move a piece that either starts or ends on
your side of the board.
The key rule is that you may only move a piece that either

In this example, Winnie may move the bishop as shown, because it ends up on the
queen side of the board. Winnie may not move the bishop to e2, because it would
start and end on the king side.
* **starts** on your side of the board, or
* **ends** on your side of the board.

In this example, Winnie may move any piece that starts or ends on the queen
side of the board, shown by the dashed rectangle. She may move the bishop as
shown by the arrow, because it ends up on the queen side of the board. Winnie
may not move the bishop to e2, because it would start and end on the king side.

![Diagram](images/new_rules/diagram2.png)

Expand All @@ -55,10 +58,10 @@ Win by check mate, as usual, but remember that the next player on the attacking
team has to be able to make the capture.

### Talking
This is a silly game, so feel free to chat with your partner, but remember that
the other team is listening. Any discussion should be heard by both teams, so
no secret codes or second languages! Of course, players should also feel free to
ignore their partner's advice.
This game shouldn't be taken too seriously, so feel free to chat with your
partner, but remember that the other team is listening. Any discussion should
be heard by both teams, so no secret codes or second languages! Of course,
players should also feel free to ignore their partner's advice.

# Broken Games
These ideas seemed promising, but didn't work at the table. Maybe I'll come back
Expand Down
22 changes: 12 additions & 10 deletions font_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@ def register_fonts(include_courier=False):
raleway_italic_file = fonts_path / 'Raleway' / 'static' / 'Raleway-Italic.ttf'
raleway_bold_italic_file = fonts_path / 'Raleway' / 'static' / 'Raleway-BoldItalic.ttf'
courier_file = fonts_path / 'Courier_Prime' / 'CourierPrime-Regular.ttf'
pdfmetrics.registerFont(TTFont("Fredoka", fredoka_file))
pdfmetrics.registerFont(TTFont("Raleway", raleway_file))
pdfmetrics.registerFont(TTFont("Raleway-Bold", raleway_bold_file))
pdfmetrics.registerFont(TTFont("Raleway-Italic", raleway_italic_file))
pdfmetrics.registerFont(TTFont("Raleway-BoldItalic", raleway_bold_italic_file))
pdfmetrics.registerFontFamily('Raleway',
'Raleway',
'Raleway-Bold',
'Raleway-Italic',
'Raleway-BoldItalic')
# If we use the regular names, then there can be collisions between fonts in
# SVG diagrams and the main body of the document.
pdfmetrics.registerFont(TTFont("Heading", fredoka_file))
pdfmetrics.registerFont(TTFont("Body", raleway_file))
pdfmetrics.registerFont(TTFont("Body-Bold", raleway_bold_file))
pdfmetrics.registerFont(TTFont("Body-Italic", raleway_italic_file))
pdfmetrics.registerFont(TTFont("Body-BoldItalic", raleway_bold_italic_file))
pdfmetrics.registerFontFamily('Body',
'Body',
'Body-Bold',
'Body-Italic',
'Body-BoldItalic')
if include_courier:
pdfmetrics.registerFont(TTFont("Courier", courier_file))
pdfmetrics.registerFontFamily('Courier',
Expand Down
12 changes: 6 additions & 6 deletions publish_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,21 +200,21 @@ def main():
if hasattr(style, 'fontSize'):
if style.name.startswith('Heading'):
scale = 1.5
style.fontName = 'Fredoka'
style.fontName = 'Heading'
else:
scale = 2
style.fontName = 'Raleway'
style.fontName = 'Body'
if False and args.booklet:
style.fontSize *= scale
style.leading *= scale
paragraph_style = styles[Styles.Normal]
numbered_list_style = ListStyle('default_list',
bulletFontName='Raleway',
bulletFontName='Body',
bulletFontSize=paragraph_style.fontSize,
leftIndent=paragraph_style.fontSize * 1.5,
bulletFormat='%s.')
bulleted_list_style = ListStyle('default_list',
bulletFontName='Raleway',
bulletFontName='Body',
bulletFontSize=paragraph_style.fontSize,
leftIndent=paragraph_style.fontSize * 1.5)
centred_style = ParagraphStyle('Author',
Expand Down Expand Up @@ -265,7 +265,7 @@ def main():
subtitle_style = ParagraphStyle('Subtitle',
parent=paragraph_style,
alignment=TA_CENTER,
fontName='Raleway-Italic')
fontName='Body-Italic')
story.append(Paragraph(subtitle_text, subtitle_style))
if args.booklet:
story.append(Spacer(0, page_size[1] * 0.15))
Expand Down Expand Up @@ -362,7 +362,7 @@ def main():
', '.join(unlinked_section_names))
raise ValueError(unknown_section_message)
doc.multiBuild(story, canvasmaker=partial(FooterCanvas,
font_name='Raleway',
font_name='Body',
is_booklet=args.booklet))
if not args.no_merge:
with merged_path.open('w') as merged_file:
Expand Down
22 changes: 13 additions & 9 deletions raw_rules/new_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ play order would be Walter, Betty, Winnie, Bob, Walter, Betty, and so on.
text: 4: Bob, -1.5, 8

### Rule changes
The key rule is that you may only move a piece that either starts or ends on
your side of the board.
The key rule is that you may only move a piece that either

In this example, Winnie may move the bishop as shown, because it ends up on the
queen side of the board. Winnie may not move the bishop to e2, because it would
start and end on the king side.
* **starts** on your side of the board, or
* **ends** on your side of the board.

In this example, Winnie may move any piece that starts or ends on the queen
side of the board, shown by the dashed rectangle. She may move the bishop as
shown by the arrow, because it ends up on the queen side of the board. Winnie
may not move the bishop to e2, because it would start and end on the king side.

r n b q k b n r
p p p p . p p p
Expand All @@ -57,6 +60,7 @@ start and end on the king side.
text: 2: Betty, 10.5, 8
text: 3: Winnie, -1.5, 1
text: 4: Bob, -1.5, 8
rect: 1, 1, 4, 8

If a player has no pieces on their side and can't move any pieces to their side,
they move nothing on that turn.
Expand All @@ -71,10 +75,10 @@ Win by check mate, as usual, but remember that the next player on the attacking
team has to be able to make the capture.

### Talking
This is a silly game, so feel free to chat with your partner, but remember that
the other team is listening. Any discussion should be heard by both teams, so
no secret codes or second languages! Of course, players should also feel free to
ignore their partner's advice.
This game shouldn't be taken too seriously, so feel free to chat with your
partner, but remember that the other team is listening. Any discussion should
be heard by both teams, so no secret codes or second languages! Of course,
players should also feel free to ignore their partner's advice.

# Broken Games
These ideas seemed promising, but didn't work at the table. Maybe I'll come back
Expand Down
5 changes: 4 additions & 1 deletion svg_diagram.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import typing
from io import BytesIO, StringIO
from pathlib import Path

import reportlab.graphics.shapes as reportlab_shapes
from cairosvg import svg2png
Expand All @@ -15,7 +16,9 @@ def to_reportlab(self) -> reportlab_shapes.Drawing:
drawing = svg2rlg(BytesIO(svg_bytes))
return drawing

def to_cairo(self, png_file: str | typing.BinaryIO):
def to_cairo(self, png_file: str | Path | typing.BinaryIO):
if isinstance(png_file, Path):
png_file = str(png_file)
svg2png(file_obj=StringIO(self.svg_text),
write_to=png_file,
background_color='transparent')
59 changes: 57 additions & 2 deletions tests/test_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def test_text(diagram_differ: DiagramDiffer):


# noinspection DuplicatedCode
def test_offset(diagram_differ: DiagramDiffer):
def test_margins(diagram_differ: DiagramDiffer):
diagram_text = dedent("""\
. . . k q . . .
. . . . . . . .
Expand Down Expand Up @@ -146,7 +146,6 @@ def test_offset(diagram_differ: DiagramDiffer):
extra = Drawing(size=(300, 240))
extra.add(extra.text('A2', (37.5, 320), **text_args))
extra.add(extra.text('G3', (307.5, 275), **text_args))
# extra.add(extra.rect((390+45, 15), (45, 45)))
expected_tree.extend(extra.get_xml())
expected_diagram = SvgDiagram(ET.tostring(expected_tree,
encoding='unicode'))
Expand All @@ -157,6 +156,62 @@ def test_offset(diagram_differ: DiagramDiffer):
diagram_differ.assert_equal(svg_diagram, expected_diagram)


# noinspection DuplicatedCode
def test_rect(diagram_differ: DiagramDiffer):
diagram_text = dedent("""\
. . . k q . . .
. . . . . . . .
. . . . . . . .
. . . . Q . . .
. . . P . . . .
. . . . . . . .
. . . . . . . .
. . . K . . . .
rect: 2, 6, 5, 7
""")
expected_text = diagram_text.split('rect')[0]
expected_board = parse_board(expected_text)
expected_board_svg = chess.svg.board(
expected_board,
size=195)
Diagram.register_svg()
expected_tree = ET.fromstring(expected_board_svg)
extra = Drawing(size=(300, 240))
extra.add(extra.rect((60, 60),
(180, 90),
fill_opacity=0,
stroke='blue',
stroke_dasharray='7.5',
stroke_width=5))
expected_tree.extend(extra.get_xml())
expected_diagram = SvgDiagram(ET.tostring(expected_tree,
encoding='unicode'))

diagram = Diagram(390, 195, diagram_text)
svg_diagram = diagram.build()

diagram_differ.assert_equal(svg_diagram, expected_diagram)


# noinspection DuplicatedCode
def test_unknown(diagram_differ: DiagramDiffer):
diagram_text = dedent("""\
. . . k q . . .
. . . . . . . .
. . . . . . . .
. . . . Q . . .
. . . P . . . .
. . . . . . . .
. . . . . . . .
. . . K . . . .
bogus: What is this?
""")

diagram = Diagram(570, 240, diagram_text)
with pytest.raises(ValueError, match=r'Unknown diagram command: bogus'):
diagram.build()


# noinspection DuplicatedCode
def test_masquerade(diagram_differ: DiagramDiffer):
diagram_text = dedent("""\
Expand Down

0 comments on commit 4916e73

Please sign in to comment.