File size: 4,324 Bytes
70830d6 683d749 70830d6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
RED = 1
YELLOW = -1
EMPTY = 0
def to_svg(board):
"""
Create an SVG representation of the board, with the latest piece dropping down via SVG
I must confess that this function was written almost entirely by Claude; done in 15 mins,
when it would have taken me a couple of hours. Amazing!
"""
svg = '''
<div style="display: flex; justify-content: center;">
<svg width="450" height="420" viewBox="0 0 450 420">
<!-- Definitions for gradients and clips -->
<defs>
<radialGradient id="redGradient" cx="0.5" cy="0.3" r="0.7">
<stop offset="0%" stop-color="#ff6666"/>
<stop offset="100%" stop-color="#cc0000"/>
</radialGradient>
<radialGradient id="yellowGradient" cx="0.5" cy="0.3" r="0.7">
<stop offset="0%" stop-color="#ffff88"/>
<stop offset="100%" stop-color="#cccc00"/>
</radialGradient>
<linearGradient id="emptyGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="100%" stop-color="#e0e0e0"/>
</linearGradient>
<!-- Define the mask for the holes -->
<mask id="holes">
<rect x="25" y="25" width="400" height="320" fill="white"/>
'''
# Add the holes to the mask
svg += ''.join(f'''
<circle
cx="{(x * 50) + 75}"
cy="{(y * 50) + 60}"
r="20"
fill="black"
/>
'''
for y in range(6)
for x, cell in enumerate(board.cells[5-y])
)
svg += '''
</mask>
</defs>
<!-- Stand -->
<path d="M0 360 L25 300 H425 L450 360 L425 385 H25 Z" fill="#004fa3"/>
<!-- Game pieces (will show through the holes) -->
'''
# Add pieces
svg += ''.join(f'''
<circle
class="{f'new-piece' if x == board.latest_x and y == (5-board.latest_y) else ''}"
cx="{(x * 50) + 75}"
cy="{(y * 50) + 60}"
r="20"
fill="{
'url(#redGradient)' if (cell == RED) else
'url(#yellowGradient)' if (cell == YELLOW) else
'none'
}"
stroke="{
'#cc0000' if (cell == RED) else
'#cccc00' if (cell == YELLOW) else
'none'
}"
stroke-width="1"
/>
<circle
class="{f'new-piece-highlight' if x == board.latest_x and y == (5-board.latest_y) else ''}"
cx="{(x * 50) + 75 - 5}"
cy="{(y * 50) + 60 - 5}"
r="8"
fill="{
'#ff8888' if (cell == RED) else
'#ffff99' if (cell == YELLOW) else
'none'
}"
opacity="0.3"
/>
'''
for y in range(6)
for x, cell in enumerate(board.cells[5-y])
if cell != EMPTY
)
svg += '''
<!-- Board overlay with holes -->
<rect x="25" y="25" width="400" height="320" fill="#0066cc" rx="10" mask="url(#holes)"/>
<!-- Hole borders (on top of everything for better 3D effect) -->
'''
# Add hole borders on top
svg += ''.join(f'''
<circle
cx="{(x * 50) + 75}"
cy="{(y * 50) + 60}"
r="20"
fill="none"
stroke="#005ab3"
stroke-width="2"
/>
'''
for y in range(6)
for x, cell in enumerate(board.cells[5-y])
)
svg += '''
</svg>
</div>
<style>
.new-piece {
animation: dropPiece 0.5s cubic-bezier(0.95, 0.05, 1, 0.5);
}
.new-piece-highlight {
animation: dropPiece 0.5s cubic-bezier(0.95, 0.05, 1, 0.5);
}
@keyframes dropPiece {
from {
transform: translateY(-300px);
}
to {
transform: translateY(0);
}
}
</style>
'''
return svg |