refactor(rithmomachia): extract guide sections into separate files
- Extract OverviewSection, PiecesSection, CaptureSection, HarmonySection, and VictorySection into separate files in guide-sections/ directory - Reduce PlayingGuideModal.tsx from 1628 lines to 519 lines - Remove unused imports from all refactored files - Each section is now independently maintainable and testable This refactoring makes it easier to enhance individual sections (e.g., adding comprehensive content to Harmony section). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,306 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
import { RithmomachiaBoard, type ExamplePiece } from '../RithmomachiaBoard'
|
||||
|
||||
export function CaptureSection({ useNativeAbacusNumbers }: { useNativeAbacusNumbers: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// Example board positions for captures
|
||||
const equalityExample: ExamplePiece[] = [
|
||||
{ square: 'G4', type: 'C', color: 'W', value: 25 }, // White's 25
|
||||
{ square: 'H4', type: 'C', color: 'B', value: 25 }, // Black's 25 (can be captured)
|
||||
]
|
||||
|
||||
const multipleExample: ExamplePiece[] = [
|
||||
{ square: 'E5', type: 'S', color: 'W', value: 64 }, // White's 64
|
||||
{ square: 'F5', type: 'T', color: 'B', value: 16 }, // Black's 16 (can be captured: 64÷16=4)
|
||||
]
|
||||
|
||||
const sumExample: ExamplePiece[] = [
|
||||
{ square: 'F4', type: 'C', color: 'W', value: 9 }, // White's 9 (attacker)
|
||||
{ square: 'E5', type: 'T', color: 'W', value: 16 }, // White's 16 (helper)
|
||||
{ square: 'G4', type: 'C', color: 'B', value: 25 }, // Black's 25 (target: 9+16=25)
|
||||
]
|
||||
|
||||
const differenceExample: ExamplePiece[] = [
|
||||
{ square: 'F4', type: 'T', color: 'W', value: 30 }, // White's 30 (attacker)
|
||||
{ square: 'E5', type: 'C', color: 'W', value: 10 }, // White's 10 (helper)
|
||||
{ square: 'G4', type: 'T', color: 'B', value: 20 }, // Black's 20 (target: 30-10=20)
|
||||
]
|
||||
|
||||
const productExample: ExamplePiece[] = [
|
||||
{ square: 'F4', type: 'C', color: 'W', value: 5 }, // White's 5 (attacker)
|
||||
{ square: 'E5', type: 'C', color: 'W', value: 5 }, // White's 5 (helper)
|
||||
{ square: 'G4', type: 'C', color: 'B', value: 25 }, // Black's 25 (target: 5×5=25)
|
||||
]
|
||||
|
||||
return (
|
||||
<div data-section="capture">
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '20px', md: '24px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '16px',
|
||||
})}
|
||||
>
|
||||
{t('guide.capture.title', 'How to Capture')}
|
||||
</h3>
|
||||
<p className={css({ fontSize: '15px', lineHeight: '1.6', mb: '24px', color: '#374151' })}>
|
||||
{t(
|
||||
'guide.capture.description',
|
||||
'You can only capture an enemy piece if your piece value has a mathematical relation to theirs:'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<h4
|
||||
className={css({
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
color: '#111827',
|
||||
mb: '12px',
|
||||
mt: '20px',
|
||||
})}
|
||||
>
|
||||
{t('guide.capture.simpleTitle', 'Simple Relations (no helper needed)')}
|
||||
</h4>
|
||||
|
||||
{/* Equality */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '20px',
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e5e7eb',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#111827', mb: '8px' })}>
|
||||
{t('guide.capture.equality', 'Equal')}
|
||||
</p>
|
||||
<p className={css({ fontSize: '13px', color: '#6b7280', mb: '12px' })}>
|
||||
{t('guide.capture.equalityExample', 'Your 25 captures their 25')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={equalityExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['F3', 'I5']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#9ca3af',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.capture.equalityCaption',
|
||||
'White Circle (25) can capture Black Circle (25) by equality'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Multiple/Divisor */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '20px',
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e5e7eb',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#111827', mb: '8px' })}>
|
||||
{t('guide.capture.multiple', 'Multiple / Divisor')}
|
||||
</p>
|
||||
<p className={css({ fontSize: '13px', color: '#6b7280', mb: '12px' })}>
|
||||
{t('guide.capture.multipleExample', 'Your 64 captures their 16 (64 ÷ 16 = 4)')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={multipleExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D4', 'G6']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#9ca3af',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.capture.multipleCaption',
|
||||
'White Square (64) can capture Black Triangle (16) because 64 ÷ 16 = 4'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h4
|
||||
className={css({
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
color: '#111827',
|
||||
mb: '12px',
|
||||
mt: '24px',
|
||||
})}
|
||||
>
|
||||
{t('guide.capture.advancedTitle', 'Advanced Relations (need one helper piece)')}
|
||||
</h4>
|
||||
|
||||
{/* Sum */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '20px',
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e5e7eb',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#111827', mb: '8px' })}>
|
||||
{t('guide.capture.sum', 'Sum')}
|
||||
</p>
|
||||
<p className={css({ fontSize: '13px', color: '#6b7280', mb: '12px' })}>
|
||||
{t('guide.capture.sumExample', 'Your 9 + helper 16 = enemy 25')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={sumExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D3', 'H6']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#9ca3af',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.capture.sumCaption',
|
||||
'White Circle (9) can capture Black Circle (25) using helper Triangle (16): 9 + 16 = 25'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Difference */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '20px',
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e5e7eb',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#111827', mb: '8px' })}>
|
||||
{t('guide.capture.difference', 'Difference')}
|
||||
</p>
|
||||
<p className={css({ fontSize: '13px', color: '#6b7280', mb: '12px' })}>
|
||||
{t('guide.capture.differenceExample', 'Your 30 - helper 10 = enemy 20')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={differenceExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D3', 'H6']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#9ca3af',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.capture.differenceCaption',
|
||||
'White Triangle (30) can capture Black Triangle (20) using helper Circle (10): 30 - 10 = 20'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Product */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '20px',
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e5e7eb',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#111827', mb: '8px' })}>
|
||||
{t('guide.capture.product', 'Product')}
|
||||
</p>
|
||||
<p className={css({ fontSize: '13px', color: '#6b7280', mb: '12px' })}>
|
||||
{t('guide.capture.productExample', 'Your 5 × helper 5 = enemy 25')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={productExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D3', 'H6']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#9ca3af',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.capture.productCaption',
|
||||
'White Circle (5) can capture Black Circle (25) using helper Circle (5): 5 × 5 = 25'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={css({
|
||||
mt: '24px',
|
||||
p: '16px',
|
||||
bg: 'rgba(59, 130, 246, 0.1)',
|
||||
borderLeft: '4px solid #3b82f6',
|
||||
borderRadius: '4px',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#1e40af', mb: '8px' })}>
|
||||
{t('guide.capture.helpersTitle', '💡 What are helpers?')}
|
||||
</p>
|
||||
<p className={css({ fontSize: '14px', color: '#1e3a8a', lineHeight: '1.6' })}>
|
||||
{t(
|
||||
'guide.capture.helpersDescription',
|
||||
'Helpers are your other pieces still on the board. They stay where they are and just provide their value for the math. The game shows you valid captures when you select a piece.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
import { RithmomachiaBoard, type ExamplePiece } from '../RithmomachiaBoard'
|
||||
|
||||
export function HarmonySection({ useNativeAbacusNumbers }: { useNativeAbacusNumbers: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// Example board positions for harmonies (White pieces in Black's territory: rows 5-8)
|
||||
const arithmeticExample: ExamplePiece[] = [
|
||||
{ square: 'E6', type: 'C', color: 'W', value: 6 }, // White's 6
|
||||
{ square: 'F6', type: 'C', color: 'W', value: 9 }, // White's 9 (middle)
|
||||
{ square: 'G6', type: 'T', color: 'W', value: 12 }, // White's 12 (arithmetic: 9 = (6+12)/2)
|
||||
]
|
||||
|
||||
const geometricExample: ExamplePiece[] = [
|
||||
{ square: 'E6', type: 'C', color: 'W', value: 4 }, // White's 4
|
||||
{ square: 'F6', type: 'C', color: 'W', value: 8 }, // White's 8 (middle)
|
||||
{ square: 'G6', type: 'T', color: 'W', value: 16 }, // White's 16 (geometric: 8² = 4×16 = 64)
|
||||
]
|
||||
|
||||
const harmonicExample: ExamplePiece[] = [
|
||||
{ square: 'E6', type: 'C', color: 'W', value: 6 }, // White's 6
|
||||
{ square: 'F6', type: 'C', color: 'W', value: 8 }, // White's 8 (middle)
|
||||
{ square: 'G6', type: 'T', color: 'W', value: 12 }, // White's 12 (harmonic: 2×6×12 = 144 = 8×18)
|
||||
]
|
||||
|
||||
return (
|
||||
<div data-section="harmony">
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '20px', md: '24px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '16px',
|
||||
})}
|
||||
>
|
||||
{t('guide.harmony.title', 'Harmonies (Progressions)')}
|
||||
</h3>
|
||||
<p className={css({ fontSize: '15px', lineHeight: '1.6', mb: '24px', color: '#374151' })}>
|
||||
{t(
|
||||
'guide.harmony.description',
|
||||
'Get 3 of your pieces into enemy territory forming one of these progressions:'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className={css({ display: 'flex', flexDirection: 'column', gap: '20px' })}>
|
||||
{/* Arithmetic Progression */}
|
||||
<div
|
||||
className={css({
|
||||
p: '16px',
|
||||
bg: '#f0fdf4',
|
||||
border: '2px solid #86efac',
|
||||
borderRadius: '8px',
|
||||
})}
|
||||
>
|
||||
<h4
|
||||
className={css({ fontSize: '16px', fontWeight: 'bold', color: '#15803d', mb: '8px' })}
|
||||
>
|
||||
{t('guide.harmony.arithmetic', 'Arithmetic Progression')}
|
||||
</h4>
|
||||
<p className={css({ fontSize: '14px', color: '#166534', mb: '8px' })}>
|
||||
{t('guide.harmony.arithmeticDesc', 'Middle value is the average')}
|
||||
</p>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '13px',
|
||||
color: '#16a34a',
|
||||
fontFamily: 'monospace',
|
||||
mb: '12px',
|
||||
})}
|
||||
>
|
||||
{t('guide.harmony.arithmeticExample', 'Example: 6, 9, 12 (because 9 = (6+12)/2)')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={arithmeticExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D5', 'H7']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#166534',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.harmony.arithmeticCaption',
|
||||
'White pieces 6, 9, 12 in a row in enemy territory form an arithmetic progression'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Geometric Progression */}
|
||||
<div
|
||||
className={css({
|
||||
p: '16px',
|
||||
bg: '#fef3c7',
|
||||
border: '2px solid #fcd34d',
|
||||
borderRadius: '8px',
|
||||
})}
|
||||
>
|
||||
<h4
|
||||
className={css({ fontSize: '16px', fontWeight: 'bold', color: '#92400e', mb: '8px' })}
|
||||
>
|
||||
{t('guide.harmony.geometric', 'Geometric Progression')}
|
||||
</h4>
|
||||
<p className={css({ fontSize: '14px', color: '#78350f', mb: '8px' })}>
|
||||
{t('guide.harmony.geometricDesc', 'Middle value is geometric mean')}
|
||||
</p>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '13px',
|
||||
color: '#a16207',
|
||||
fontFamily: 'monospace',
|
||||
mb: '12px',
|
||||
})}
|
||||
>
|
||||
{t('guide.harmony.geometricExample', 'Example: 4, 8, 16 (because 8² = 4×16)')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={geometricExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D5', 'H7']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#78350f',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.harmony.geometricCaption',
|
||||
'White pieces 4, 8, 16 in a row in enemy territory form a geometric progression'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Harmonic Progression */}
|
||||
<div
|
||||
className={css({
|
||||
p: '16px',
|
||||
bg: '#dbeafe',
|
||||
border: '2px solid #93c5fd',
|
||||
borderRadius: '8px',
|
||||
})}
|
||||
>
|
||||
<h4
|
||||
className={css({ fontSize: '16px', fontWeight: 'bold', color: '#1e40af', mb: '8px' })}
|
||||
>
|
||||
{t('guide.harmony.harmonic', 'Harmonic Progression')}
|
||||
</h4>
|
||||
<p className={css({ fontSize: '14px', color: '#1e3a8a', mb: '8px' })}>
|
||||
{t('guide.harmony.harmonicDesc', 'Special proportion (formula: 2AB = M(A+B))')}
|
||||
</p>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '13px',
|
||||
color: '#2563eb',
|
||||
fontFamily: 'monospace',
|
||||
mb: '12px',
|
||||
})}
|
||||
>
|
||||
{t('guide.harmony.harmonicExample', 'Example: 6, 8, 12 (because 2×6×12 = 8×(6+12))')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={harmonicExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['D5', 'H7']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#1e3a8a',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.harmony.harmonicCaption',
|
||||
'White pieces 6, 8, 12 in a row in enemy territory form a harmonic progression'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={css({
|
||||
mt: '24px',
|
||||
p: '16px',
|
||||
bg: 'rgba(239, 68, 68, 0.1)',
|
||||
borderLeft: '4px solid #ef4444',
|
||||
borderRadius: '4px',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '14px', fontWeight: 'bold', color: '#991b1b', mb: '8px' })}>
|
||||
{t('guide.harmony.rulesTitle', '⚠️ Important Rules')}
|
||||
</p>
|
||||
<ul className={css({ fontSize: '14px', color: '#7f1d1d', lineHeight: '1.8', pl: '20px' })}>
|
||||
<li>
|
||||
{t(
|
||||
'guide.harmony.rule1',
|
||||
'Your 3 pieces must be in a straight line (row, column, or diagonal)'
|
||||
)}
|
||||
</li>
|
||||
<li>{t('guide.harmony.rule2', 'All 3 must be in enemy territory')}</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.harmony.rule3',
|
||||
'When you form a harmony, your opponent gets one turn to break it'
|
||||
)}
|
||||
</li>
|
||||
<li>{t('guide.harmony.rule4', 'If it survives, you win!')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
import { RithmomachiaBoard, type ExamplePiece } from '../RithmomachiaBoard'
|
||||
|
||||
export function OverviewSection({ useNativeAbacusNumbers }: { useNativeAbacusNumbers: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// Initial board setup - full starting position
|
||||
const initialSetup: ExamplePiece[] = [
|
||||
// BLACK - Column A
|
||||
{ square: 'A1', type: 'S', color: 'B', value: 28 },
|
||||
{ square: 'A2', type: 'S', color: 'B', value: 66 },
|
||||
{ square: 'A7', type: 'S', color: 'B', value: 225 },
|
||||
{ square: 'A8', type: 'S', color: 'B', value: 361 },
|
||||
// BLACK - Column B
|
||||
{ square: 'B1', type: 'S', color: 'B', value: 28 },
|
||||
{ square: 'B2', type: 'S', color: 'B', value: 66 },
|
||||
{ square: 'B3', type: 'T', color: 'B', value: 36 },
|
||||
{ square: 'B4', type: 'T', color: 'B', value: 30 },
|
||||
{ square: 'B5', type: 'T', color: 'B', value: 56 },
|
||||
{ square: 'B6', type: 'T', color: 'B', value: 64 },
|
||||
{ square: 'B7', type: 'S', color: 'B', value: 120 },
|
||||
{ square: 'B8', type: 'P', color: 'B', value: 36 },
|
||||
// BLACK - Column C
|
||||
{ square: 'C1', type: 'T', color: 'B', value: 16 },
|
||||
{ square: 'C2', type: 'T', color: 'B', value: 12 },
|
||||
{ square: 'C3', type: 'C', color: 'B', value: 9 },
|
||||
{ square: 'C4', type: 'C', color: 'B', value: 25 },
|
||||
{ square: 'C5', type: 'C', color: 'B', value: 49 },
|
||||
{ square: 'C6', type: 'C', color: 'B', value: 81 },
|
||||
{ square: 'C7', type: 'T', color: 'B', value: 90 },
|
||||
{ square: 'C8', type: 'T', color: 'B', value: 100 },
|
||||
// BLACK - Column D
|
||||
{ square: 'D3', type: 'C', color: 'B', value: 3 },
|
||||
{ square: 'D4', type: 'C', color: 'B', value: 5 },
|
||||
{ square: 'D5', type: 'C', color: 'B', value: 7 },
|
||||
{ square: 'D6', type: 'C', color: 'B', value: 9 },
|
||||
// WHITE - Column M
|
||||
{ square: 'M3', type: 'C', color: 'W', value: 8 },
|
||||
{ square: 'M4', type: 'C', color: 'W', value: 6 },
|
||||
{ square: 'M5', type: 'C', color: 'W', value: 4 },
|
||||
{ square: 'M6', type: 'C', color: 'W', value: 2 },
|
||||
// WHITE - Column N
|
||||
{ square: 'N1', type: 'T', color: 'W', value: 81 },
|
||||
{ square: 'N2', type: 'T', color: 'W', value: 72 },
|
||||
{ square: 'N3', type: 'C', color: 'W', value: 64 },
|
||||
{ square: 'N4', type: 'C', color: 'W', value: 16 },
|
||||
{ square: 'N5', type: 'C', color: 'W', value: 16 },
|
||||
{ square: 'N6', type: 'C', color: 'W', value: 4 },
|
||||
{ square: 'N7', type: 'T', color: 'W', value: 6 },
|
||||
{ square: 'N8', type: 'T', color: 'W', value: 9 },
|
||||
// WHITE - Column O
|
||||
{ square: 'O1', type: 'S', color: 'W', value: 153 },
|
||||
{ square: 'O2', type: 'P', color: 'W', value: 64 },
|
||||
{ square: 'O3', type: 'T', color: 'W', value: 72 },
|
||||
{ square: 'O4', type: 'T', color: 'W', value: 20 },
|
||||
{ square: 'O5', type: 'T', color: 'W', value: 20 },
|
||||
{ square: 'O6', type: 'T', color: 'W', value: 25 },
|
||||
{ square: 'O7', type: 'S', color: 'W', value: 45 },
|
||||
{ square: 'O8', type: 'S', color: 'W', value: 15 },
|
||||
// WHITE - Column P
|
||||
{ square: 'P1', type: 'S', color: 'W', value: 289 },
|
||||
{ square: 'P2', type: 'S', color: 'W', value: 169 },
|
||||
{ square: 'P7', type: 'S', color: 'W', value: 81 },
|
||||
{ square: 'P8', type: 'S', color: 'W', value: 25 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div data-section="overview">
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '20px', md: '24px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '16px',
|
||||
})}
|
||||
>
|
||||
{t('guide.overview.goalTitle', 'Goal of the Game')}
|
||||
</h3>
|
||||
<p className={css({ fontSize: '16px', lineHeight: '1.6', mb: '20px', color: '#374151' })}>
|
||||
{t(
|
||||
'guide.overview.goal',
|
||||
'Arrange 3 of your pieces in enemy territory to form a mathematical progression, survive one opponent turn, and win.'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '18px', md: '20px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '12px',
|
||||
mt: '24px',
|
||||
})}
|
||||
>
|
||||
{t('guide.overview.boardTitle', 'The Board')}
|
||||
</h3>
|
||||
|
||||
<div className={css({ mb: '20px' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={initialSetup}
|
||||
scale={0.6}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className={css({ fontSize: '14px', color: '#6b7280', mb: '20px', fontStyle: 'italic' })}>
|
||||
{t(
|
||||
'guide.overview.boardCaption',
|
||||
'The starting position - Black on the left, White on the right'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<ul
|
||||
className={css({
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.8',
|
||||
pl: '20px',
|
||||
mb: '20px',
|
||||
color: '#374151',
|
||||
})}
|
||||
>
|
||||
<li>{t('guide.overview.boardSize', '8 rows × 16 columns (columns A-P, rows 1-8)')}</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.overview.territory',
|
||||
'Your half: Black controls rows 5-8, White controls rows 1-4'
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.overview.enemyTerritory',
|
||||
'Enemy territory: Where you need to build your winning progression'
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '18px', md: '20px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '12px',
|
||||
mt: '24px',
|
||||
})}
|
||||
>
|
||||
{t('guide.overview.howToPlayTitle', 'How to Play')}
|
||||
</h3>
|
||||
<ol
|
||||
className={css({
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.8',
|
||||
pl: '20px',
|
||||
color: '#374151',
|
||||
})}
|
||||
>
|
||||
<li>{t('guide.overview.step1', 'Move your pieces toward the center')}</li>
|
||||
<li>{t('guide.overview.step2', 'Look for ways to capture using math')}</li>
|
||||
<li>{t('guide.overview.step3', 'Push into enemy territory')}</li>
|
||||
<li>{t('guide.overview.step4', 'Watch for chances to make a progression')}</li>
|
||||
<li>{t('guide.overview.step5', 'Win by forming a progression that survives one turn!')}</li>
|
||||
</ol>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
import { PieceRenderer } from '../PieceRenderer'
|
||||
import type { PieceType } from '../../types'
|
||||
|
||||
export function PiecesSection({ useNativeAbacusNumbers }: { useNativeAbacusNumbers: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
const pieces: {
|
||||
type: PieceType
|
||||
name: string
|
||||
movement: string
|
||||
count: number
|
||||
exampleValues: number[]
|
||||
}[] = [
|
||||
{
|
||||
type: 'C',
|
||||
name: t('guide.pieces.circle', 'Circle'),
|
||||
movement: t('guide.pieces.circleMove', 'Moves diagonally'),
|
||||
count: 12,
|
||||
exampleValues: [3, 5, 7, 9],
|
||||
},
|
||||
{
|
||||
type: 'T',
|
||||
name: t('guide.pieces.triangle', 'Triangle'),
|
||||
movement: t('guide.pieces.triangleMove', 'Moves in straight lines'),
|
||||
count: 6,
|
||||
exampleValues: [12, 16, 20, 30],
|
||||
},
|
||||
{
|
||||
type: 'S',
|
||||
name: t('guide.pieces.square', 'Square'),
|
||||
movement: t('guide.pieces.squareMove', 'Moves in any direction'),
|
||||
count: 6,
|
||||
exampleValues: [25, 28, 45, 66],
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div data-section="pieces">
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '20px', md: '24px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '16px',
|
||||
})}
|
||||
>
|
||||
{t('guide.pieces.title', 'Your Pieces (25 total)')}
|
||||
</h3>
|
||||
<p className={css({ fontSize: '15px', mb: '24px', color: '#374151' })}>
|
||||
{t(
|
||||
'guide.pieces.description',
|
||||
'Each side has 25 pieces with different movement patterns. The shape tells you how it moves:'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className={css({ display: 'flex', flexDirection: 'column', gap: '24px' })}>
|
||||
{pieces.map((piece) => (
|
||||
<div
|
||||
key={piece.type}
|
||||
className={css({
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e5e7eb',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({ display: 'flex', alignItems: 'center', gap: '12px', mb: '12px' })}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
flexShrink: 0,
|
||||
})}
|
||||
>
|
||||
<PieceRenderer
|
||||
type={piece.type}
|
||||
color="W"
|
||||
value={piece.exampleValues[0]}
|
||||
size={60}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<div className={css({ flex: 1 })}>
|
||||
<h4
|
||||
className={css({
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
color: '#111827',
|
||||
mb: '4px',
|
||||
})}
|
||||
>
|
||||
{piece.name} ({piece.count} per side)
|
||||
</h4>
|
||||
<p className={css({ fontSize: '14px', color: '#6b7280' })}>{piece.movement}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Example values */}
|
||||
<div className={css({ mt: '12px' })}>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '13px',
|
||||
color: '#9ca3af',
|
||||
mb: '8px',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t('guide.pieces.exampleValues', 'Example values')}:
|
||||
</p>
|
||||
<div className={css({ display: 'flex', gap: '12px', flexWrap: 'wrap' })}>
|
||||
{piece.exampleValues.map((value) => (
|
||||
<div
|
||||
key={value}
|
||||
className={css({
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
})}
|
||||
>
|
||||
<PieceRenderer
|
||||
type={piece.type}
|
||||
color="W"
|
||||
value={value}
|
||||
size={48}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Pyramid section */}
|
||||
<div
|
||||
className={css({
|
||||
mt: '32px',
|
||||
p: '20px',
|
||||
bg: 'rgba(251, 191, 36, 0.1)',
|
||||
borderLeft: '4px solid #f59e0b',
|
||||
borderRadius: '4px',
|
||||
})}
|
||||
>
|
||||
<h4 className={css({ fontSize: '18px', fontWeight: 'bold', color: '#92400e', mb: '12px' })}>
|
||||
{t('guide.pieces.pyramidTitle', '⭐ Pyramids are special')}
|
||||
</h4>
|
||||
<p className={css({ fontSize: '14px', color: '#78350f', lineHeight: '1.6', mb: '16px' })}>
|
||||
{t(
|
||||
'guide.pieces.pyramidDescription',
|
||||
'Each side has 1 Pyramid. Pyramids have 4 face values - when capturing, you choose which value to use. They move one step in any direction.'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className={css({ display: 'flex', gap: '32px', flexWrap: 'wrap', mt: '16px' })}>
|
||||
{/* Black Pyramid */}
|
||||
<div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '13px',
|
||||
fontWeight: 'bold',
|
||||
color: '#92400e',
|
||||
mb: '8px',
|
||||
textAlign: 'center',
|
||||
})}
|
||||
>
|
||||
{t('guide.pieces.blackPyramid', 'Black Pyramid')}
|
||||
</p>
|
||||
<div className={css({ width: '80px', height: '80px' })}>
|
||||
<PieceRenderer
|
||||
type="P"
|
||||
color="B"
|
||||
value="P"
|
||||
size={80}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#78350f',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t('guide.pieces.pyramidValues', 'Values')}: 36, 25, 16, 4
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* White Pyramid */}
|
||||
<div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '13px',
|
||||
fontWeight: 'bold',
|
||||
color: '#92400e',
|
||||
mb: '8px',
|
||||
textAlign: 'center',
|
||||
})}
|
||||
>
|
||||
{t('guide.pieces.whitePyramid', 'White Pyramid')}
|
||||
</p>
|
||||
<div className={css({ width: '80px', height: '80px' })}>
|
||||
<PieceRenderer
|
||||
type="P"
|
||||
color="W"
|
||||
value="P"
|
||||
size={80}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#78350f',
|
||||
mt: '8px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t('guide.pieces.pyramidValues', 'Values')}: 64, 49, 36, 25
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
import { RithmomachiaBoard, type ExamplePiece } from '../RithmomachiaBoard'
|
||||
|
||||
export function VictorySection({ useNativeAbacusNumbers }: { useNativeAbacusNumbers: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// Example winning position: White has formed a geometric progression in Black's territory
|
||||
const winningExample: ExamplePiece[] = [
|
||||
// White's winning progression in enemy territory (rows 5-8)
|
||||
{ square: 'E6', type: 'C', color: 'W', value: 4 },
|
||||
{ square: 'F6', type: 'C', color: 'W', value: 8 },
|
||||
{ square: 'G6', type: 'T', color: 'W', value: 16 },
|
||||
// Some Black pieces remaining (unable to break the harmony)
|
||||
{ square: 'C7', type: 'S', color: 'B', value: 49 },
|
||||
{ square: 'J6', type: 'T', color: 'B', value: 30 },
|
||||
]
|
||||
|
||||
return (
|
||||
<div data-section="victory">
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '20px', md: '24px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '16px',
|
||||
})}
|
||||
>
|
||||
{t('guide.victory.title', 'How to Win')}
|
||||
</h3>
|
||||
|
||||
<div className={css({ display: 'flex', flexDirection: 'column', gap: '24px' })}>
|
||||
<div>
|
||||
<h4
|
||||
className={css({
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
color: '#111827',
|
||||
mb: '12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
})}
|
||||
>
|
||||
<span>👑</span>
|
||||
<span>{t('guide.victory.harmony', 'Victory #1: Harmony (Progression)')}</span>
|
||||
</h4>
|
||||
<p className={css({ fontSize: '15px', lineHeight: '1.6', color: '#374151', mb: '12px' })}>
|
||||
{t(
|
||||
'guide.victory.harmonyDesc',
|
||||
"Form a mathematical progression with 3 pieces in enemy territory. If it survives your opponent's next turn, you win!"
|
||||
)}
|
||||
</p>
|
||||
|
||||
{/* Visual example of winning harmony */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '16px',
|
||||
p: '16px',
|
||||
bg: '#f9fafb',
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #86efac',
|
||||
})}
|
||||
>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
color: '#15803d',
|
||||
mb: '12px',
|
||||
textAlign: 'center',
|
||||
})}
|
||||
>
|
||||
{t('guide.victory.exampleTitle', 'Example: White Wins!')}
|
||||
</p>
|
||||
<div className={css({ display: 'flex', justifyContent: 'center' })}>
|
||||
<RithmomachiaBoard
|
||||
pieces={winningExample}
|
||||
scale={0.4}
|
||||
cropToSquares={['B5', 'K8']}
|
||||
showLabels={true}
|
||||
useNativeAbacusNumbers={useNativeAbacusNumbers}
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: '12px',
|
||||
color: '#166534',
|
||||
mt: '12px',
|
||||
textAlign: 'center',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
{t(
|
||||
'guide.victory.exampleCaption',
|
||||
'White pieces 4, 8, 16 form a geometric progression in enemy territory. Black cannot break it - White wins!'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={css({
|
||||
p: '12px',
|
||||
bg: '#f0fdf4',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid #86efac',
|
||||
})}
|
||||
>
|
||||
<p className={css({ fontSize: '13px', color: '#15803d' })}>
|
||||
{t(
|
||||
'guide.victory.harmonyNote',
|
||||
'This is the primary victory condition in Rithmomachia'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4
|
||||
className={css({
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
color: '#111827',
|
||||
mb: '12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
})}
|
||||
>
|
||||
<span>🚫</span>
|
||||
<span>{t('guide.victory.exhaustion', 'Victory #2: Exhaustion')}</span>
|
||||
</h4>
|
||||
<p className={css({ fontSize: '15px', lineHeight: '1.6', color: '#374151' })}>
|
||||
{t(
|
||||
'guide.victory.exhaustionDesc',
|
||||
'If your opponent has no legal moves at the start of their turn, they lose.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: { base: '18px', md: '20px' },
|
||||
fontWeight: 'bold',
|
||||
color: '#7c2d12',
|
||||
mb: '12px',
|
||||
mt: '32px',
|
||||
})}
|
||||
>
|
||||
{t('guide.victory.strategyTitle', 'Quick Strategy Tips')}
|
||||
</h3>
|
||||
<ul
|
||||
className={css({
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.8',
|
||||
pl: '20px',
|
||||
color: '#374151',
|
||||
})}
|
||||
>
|
||||
<li>{t('guide.victory.tip1', 'Control the center — easier to invade enemy territory')}</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.victory.tip2',
|
||||
'Small pieces are fast — circles (3, 5, 7, 9) can slip into enemy half quickly'
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.victory.tip3',
|
||||
'Large pieces are powerful — harder to capture due to their size'
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.victory.tip4',
|
||||
"Watch for harmony threats — don't let opponent get 3 pieces deep in your territory"
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{t(
|
||||
'guide.victory.tip5',
|
||||
'Pyramids are flexible — choose the right face value for each situation'
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user