Skip to content

Commit

Permalink
Polish rules for Crowded House, issue #3.
Browse files Browse the repository at this point in the history
  • Loading branch information
donkirkby committed Oct 28, 2023
1 parent 4705d4a commit 59161f6
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

game-crafter
1 change: 1 addition & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 48 additions & 7 deletions diagram.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import xml.etree.ElementTree as ET # noqa

import chess.svg
from svgwrite import Drawing

Expand All @@ -6,6 +8,11 @@


class Diagram:
@staticmethod
def register_svg():
ET.register_namespace('', 'http://www.w3.org/2000/svg')
ET.register_namespace('xlink', 'http://www.w3.org/1999/xlink')

def __init__(self,
page_width: float,
page_height: float,
Expand All @@ -17,20 +24,54 @@ def __init__(self,
def build(self) -> SvgDiagram:
if self.board_state.startswith('type: '):
return self.build_grid()
self.register_svg()
lines = self.board_state.splitlines()
board = parse_board('\n'.join(lines[:8]))
arrows = []
square_size = 45
x0 = 15 - 0.5*square_size
y0 = 9.112 * square_size
text_elements = Drawing()
text_args = dict(text_anchor='middle',
font_family='Raleway',
font_size=round(0.55*square_size))
margins = (0, 0)
for line in lines[8:]:
command, body = line.split(':', maxsplit=1)
fields = [field.strip() for field in body.split(',')]
tail = getattr(chess, fields[0].upper())
head = getattr(chess, fields[1].upper())
colour = fields[2]
arrows.append(chess.svg.Arrow(tail, head, color=colour))
size = round(min(self.page_width/2, self.page_height))
svg_text = chess.svg.board(board, size=size, arrows=arrows)
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))
elif command == 'margins':
margins = tuple(float(n) for n in fields[:2])
else:
tail = getattr(chess, fields[0].upper())
head = getattr(chess, fields[1].upper())
colour = fields[2]
arrows.append(chess.svg.Arrow(tail, head, color=colour))
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
x_aspect = self.page_width/2/view_width
y_aspect = self.page_height/view_height
aspect = min(x_aspect, y_aspect)
image_width = view_width * aspect
image_height = view_height * aspect
board_size = round(original_view_size * aspect)
view_box = (f'{-square_size*margins[0]} {-square_size*margins[1]} '
f'{view_width} {view_height}')
svg_text = chess.svg.board(board, size=board_size, arrows=arrows)
board_tree = ET.fromstring(svg_text)
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())

diagram = SvgDiagram(svg_text)
diagram = SvgDiagram(ET.tostring(board_tree, encoding='unicode'))
return diagram

def build_grid(self):
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 added 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.
46 changes: 22 additions & 24 deletions docs/new_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,42 @@ filled out or change based on feedback from players.
Two teams of two play, with each player moving the pieces of their colour
on the left or right half of the board. As usual, white moves first, then
alternates with black. Each king-side player takes the first move for their
team, then alternates with their partner. As an example, imagine Walter plays
king-side white, Winnie plays queen-side white, Betty plays king-side black, and
Bob plays queen-side black. Then the play order would be Walter, Betty, Winnie,
Bob, Walter, Betty, and so on.
team, then alternates with their partner.

### Rule changes
The key rule is that you can only move a piece that either starts or ends on
your side of the board. In this example, queen-side white may move the bishop as
shown, because it ends up on the queen side of the board. Moving the bishop to
e2 would not be allowed, because it would start and end on the king side.
In the following example, Walter plays king-side white, Winnie plays queen-side
white, Betty plays king-side black, and Bob plays queen-side black. Then the
play order would be Walter, Betty, Winnie, Bob, Walter, Betty, and so on.

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

### Winning
Win by check mate, as usual, but remember that the next player on the attacking
team has to be able to make the capture.
### Rule changes
The key rule is that you may only move a piece that either starts or ends on
your side of the board.

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.

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

If a player has no pieces on their side and can't move any pieces to their side,
they move nothing on that turn.

The rest of the rule changes flow from whether a piece may be captured
immediately. A king may move into check or castle out of check, if the next
player can't make the capture. En passant capture only works if the pawn is
captured immediately after its first move.

### Winning
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.

If partners are giving too much advice or want a more challenging game, add a
chess clock. Decide on an overall time, then decide on a time cost for talking.
As an example, you might play with a start time of 30 minutes and a 2-minute
bonus for each time your opponents talk. It's probably easiest to put a handful
of coins next to the board. Whenever your team discusses a move, either move a
coin from the centre to your opponents' pile or from your pile to the centre. If
a team runs out of time, they can add 2 minutes for each coin in their pile and
put their pile back in the centre. Once a team pays a coin, they can discuss as
much as they want until they make a move.

# Broken Games
These ideas seemed promising, but didn't work at the table. Maybe I'll come back
to them, if I get inspired. Masquerade Chess seemed broken for 15 years, before
Expand All @@ -78,7 +76,7 @@ write the numbers 1 to 8 on checkers for each player. Put the black checkers on
black's back row and the light checkers on white's back row. Finally, write two
grids like this to secretly record your pieces and deduce your opponent's:

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

Obviously, you don't have to put the pieces in their standard starting
positions, but you do have to have a standard set of pieces. (You can't give
Expand All @@ -94,7 +92,7 @@ opponent learns which of your combinations are impossible.

Here's one possible way to fill in your grid at the start of the game:

![Diagram](images/new_rules/diagram3.png)
![Diagram](images/new_rules/diagram4.png)

At the start of your turn, you may guess the identity of one of your opponent's
checkers. If you guess correctly, you may make a bonus move after your regular
Expand Down
57 changes: 36 additions & 21 deletions raw_rules/new_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,33 @@ filled out or change based on feedback from players.
Two teams of two play, with each player moving the pieces of their colour
on the left or right half of the board. As usual, white moves first, then
alternates with black. Each king-side player takes the first move for their
team, then alternates with their partner. As an example, imagine Walter plays
king-side white, Winnie plays queen-side white, Betty plays king-side black, and
Bob plays queen-side black. Then the play order would be Walter, Betty, Winnie,
Bob, Walter, Betty, and so on.
team, then alternates with their partner.

In the following example, Walter plays king-side white, Winnie plays queen-side
white, Betty plays king-side black, and Bob plays queen-side black. Then the
play order would be Walter, Betty, Winnie, Bob, Walter, Betty, and so on.

r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R
margins: 3, 0
text: 1: Walter, 10.5, 1
text: 2: Betty, 10.5, 8
text: 3: Winnie, -1.5, 1
text: 4: Bob, -1.5, 8

### Rule changes
The key rule is that you can only move a piece that either starts or ends on
your side of the board. In this example, queen-side white may move the bishop as
shown, because it ends up on the queen side of the board. Moving the bishop to
e2 would not be allowed, because it would start and end on the king side.
The key rule is that you may only move a piece that either starts or ends on
your side of the board.

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.

r n b q k b n r
p p p p . p p p
Expand All @@ -35,32 +52,30 @@ e2 would not be allowed, because it would start and end on the king side.
P P P P . P P P
R N B Q K B N R
arrow: f1, c4, white
margins: 3, 0
text: 1: Walter, 10.5, 1
text: 2: Betty, 10.5, 8
text: 3: Winnie, -1.5, 1
text: 4: Bob, -1.5, 8

### Winning
Win by check mate, as usual, but remember that the next player on the attacking
team has to be able to make the capture.
If a player has no pieces on their side and can't move any pieces to their side,
they move nothing on that turn.

The rest of the rule changes flow from whether a piece may be captured
immediately. A king may move into check or castle out of check, if the next
player can't make the capture. En passant capture only works if the pawn is
captured immediately after its first move.

### Winning
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.

If partners are giving too much advice or want a more challenging game, add a
chess clock. Decide on an overall time, then decide on a time cost for talking.
As an example, you might play with a start time of 30 minutes and a 2-minute
bonus for each time your opponents talk. It's probably easiest to put a handful
of coins next to the board. Whenever your team discusses a move, either move a
coin from the centre to your opponents' pile or from your pile to the centre. If
a team runs out of time, they can add 2 minutes for each coin in their pile and
put their pile back in the centre. Once a team pays a coin, they can discuss as
much as they want until they make a move.

# Broken Games
These ideas seemed promising, but didn't work at the table. Maybe I'll come back
to them, if I get inspired. Masquerade Chess seemed broken for 15 years, before
Expand Down
82 changes: 82 additions & 0 deletions tests/test_diagram.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from pathlib import Path
from textwrap import dedent
import xml.etree.ElementTree as ET # noqa

import chess.svg
import pytest
Expand Down Expand Up @@ -76,6 +77,87 @@ def test_arrows(diagram_differ: DiagramDiffer):
diagram_differ.assert_equal(svg_diagram, expected_diagram)


# noinspection DuplicatedCode
def test_text(diagram_differ: DiagramDiffer):
diagram_text = dedent("""\
. . . k q . . .
. . . . . . . .
. . . . . . . .
. . . . Q . . .
. . . P . . . .
. . . . . . . .
. . . . . . . .
. . . K . . . .
text: A2, 1, 2
text: G3, 7, 3
""")
text_args = dict(text_anchor='middle',
font_family='Raleway',
font_size=25)
expected_text = diagram_text.split('text')[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.text('A2', (37.5, 320), **text_args))
extra.add(extra.text('G3', (307.5, 275), **text_args))
expected_tree.extend(extra.get_xml())
expected_diagram = SvgDiagram(ET.tostring(expected_tree,
encoding='unicode'))
ET.register_namespace('', '') # Force registration again.

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

diagram_differ.assert_equal(svg_diagram, expected_diagram)


# noinspection DuplicatedCode
def test_offset(diagram_differ: DiagramDiffer):
diagram_text = dedent("""\
. . . k q . . .
. . . . . . . .
. . . . . . . .
. . . . Q . . .
. . . P . . . .
. . . . . . . .
. . . . . . . .
. . . K . . . .
margins: 2, 1
text: A2, 1, 2
text: G3, 7, 3
""")
text_args = dict(text_anchor='middle',
font_family='Raleway',
font_size=25)
expected_text = diagram_text.split('margins')[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)
expected_tree.set('width', '285')
expected_tree.set('height', '240')
expected_tree.set('viewBox', '-90 -45 570 480')
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'))

diagram = Diagram(570, 240, diagram_text)
svg_diagram = diagram.build()

diagram_differ.assert_equal(svg_diagram, expected_diagram)


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

0 comments on commit 59161f6

Please sign in to comment.