diff --git a/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx b/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx index a9a2beb8..71144373 100644 --- a/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx +++ b/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx @@ -5,10 +5,12 @@ import { useTranslation } from 'react-i18next' import { css } from '../../../../styled-system/css' import { Z_INDEX } from '@/constants/zIndex' import { useAbacusSettings } from '@/hooks/useAbacusSettings' -import { PieceRenderer } from './PieceRenderer' -import { RithmomachiaBoard, type ExamplePiece } from './RithmomachiaBoard' -import type { PieceType, Color } from '../types' import '../i18n/config' // Initialize i18n +import { OverviewSection } from './guide-sections/OverviewSection' +import { PiecesSection } from './guide-sections/PiecesSection' +import { CaptureSection } from './guide-sections/CaptureSection' +import { HarmonySection } from './guide-sections/HarmonySection' +import { VictorySection } from './guide-sections/VictorySection' interface PlayingGuideModalProps { isOpen: boolean @@ -512,1114 +514,3 @@ export function PlayingGuideModal({ isOpen, onClose, standalone = false }: Playi // Otherwise, just render the modal (no backdrop so game is visible) return modalContent } - -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 ( -
-

- {t('guide.overview.goalTitle', 'Goal of the Game')} -

-

- {t( - 'guide.overview.goal', - 'Arrange 3 of your pieces in enemy territory to form a mathematical progression, survive one opponent turn, and win.' - )} -

- -

- {t('guide.overview.boardTitle', 'The Board')} -

- -
- -
- -

- {t( - 'guide.overview.boardCaption', - 'The starting position - Black on the left, White on the right' - )} -

- - - -

- {t('guide.overview.howToPlayTitle', 'How to Play')} -

-
    -
  1. {t('guide.overview.step1', 'Move your pieces toward the center')}
  2. -
  3. {t('guide.overview.step2', 'Look for ways to capture using math')}
  4. -
  5. {t('guide.overview.step3', 'Push into enemy territory')}
  6. -
  7. {t('guide.overview.step4', 'Watch for chances to make a progression')}
  8. -
  9. {t('guide.overview.step5', 'Win by forming a progression that survives one turn!')}
  10. -
-
- ) -} - -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 ( -
-

- {t('guide.pieces.title', 'Your Pieces (25 total)')} -

-

- {t( - 'guide.pieces.description', - 'Each side has 25 pieces with different movement patterns. The shape tells you how it moves:' - )} -

- -
- {pieces.map((piece) => ( -
-
-
- -
-
-

- {piece.name} ({piece.count} per side) -

-

{piece.movement}

-
-
- - {/* Example values */} -
-

- {t('guide.pieces.exampleValues', 'Example values')}: -

-
- {piece.exampleValues.map((value) => ( -
- -
- ))} -
-
-
- ))} -
- - {/* Pyramid section */} -
-

- {t('guide.pieces.pyramidTitle', '⭐ Pyramids are special')} -

-

- {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.' - )} -

- -
- {/* Black Pyramid */} -
-

- {t('guide.pieces.blackPyramid', 'Black Pyramid')} -

-
- -
-

- {t('guide.pieces.pyramidValues', 'Values')}: 36, 25, 16, 4 -

-
- - {/* White Pyramid */} -
-

- {t('guide.pieces.whitePyramid', 'White Pyramid')} -

-
- -
-

- {t('guide.pieces.pyramidValues', 'Values')}: 64, 49, 36, 25 -

-
-
-
-
- ) -} - -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 ( -
-

- {t('guide.capture.title', 'How to Capture')} -

-

- {t( - 'guide.capture.description', - 'You can only capture an enemy piece if your piece value has a mathematical relation to theirs:' - )} -

- -

- {t('guide.capture.simpleTitle', 'Simple Relations (no helper needed)')} -

- - {/* Equality */} -
-

- {t('guide.capture.equality', 'Equal')} -

-

- {t('guide.capture.equalityExample', 'Your 25 captures their 25')} -

-
- -
-

- {t( - 'guide.capture.equalityCaption', - 'White Circle (25) can capture Black Circle (25) by equality' - )} -

-
- - {/* Multiple/Divisor */} -
-

- {t('guide.capture.multiple', 'Multiple / Divisor')} -

-

- {t('guide.capture.multipleExample', 'Your 64 captures their 16 (64 ÷ 16 = 4)')} -

-
- -
-

- {t( - 'guide.capture.multipleCaption', - 'White Square (64) can capture Black Triangle (16) because 64 ÷ 16 = 4' - )} -

-
- -

- {t('guide.capture.advancedTitle', 'Advanced Relations (need one helper piece)')} -

- - {/* Sum */} -
-

- {t('guide.capture.sum', 'Sum')} -

-

- {t('guide.capture.sumExample', 'Your 9 + helper 16 = enemy 25')} -

-
- -
-

- {t( - 'guide.capture.sumCaption', - 'White Circle (9) can capture Black Circle (25) using helper Triangle (16): 9 + 16 = 25' - )} -

-
- - {/* Difference */} -
-

- {t('guide.capture.difference', 'Difference')} -

-

- {t('guide.capture.differenceExample', 'Your 30 - helper 10 = enemy 20')} -

-
- -
-

- {t( - 'guide.capture.differenceCaption', - 'White Triangle (30) can capture Black Triangle (20) using helper Circle (10): 30 - 10 = 20' - )} -

-
- - {/* Product */} -
-

- {t('guide.capture.product', 'Product')} -

-

- {t('guide.capture.productExample', 'Your 5 × helper 5 = enemy 25')} -

-
- -
-

- {t( - 'guide.capture.productCaption', - 'White Circle (5) can capture Black Circle (25) using helper Circle (5): 5 × 5 = 25' - )} -

-
- -
-

- {t('guide.capture.helpersTitle', '💡 What are helpers?')} -

-

- {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.' - )} -

-
-
- ) -} - -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 ( -
-

- {t('guide.harmony.title', 'Harmonies (Progressions)')} -

-

- {t( - 'guide.harmony.description', - 'Get 3 of your pieces into enemy territory forming one of these progressions:' - )} -

- -
- {/* Arithmetic Progression */} -
-

- {t('guide.harmony.arithmetic', 'Arithmetic Progression')} -

-

- {t('guide.harmony.arithmeticDesc', 'Middle value is the average')} -

-

- {t('guide.harmony.arithmeticExample', 'Example: 6, 9, 12 (because 9 = (6+12)/2)')} -

-
- -
-

- {t( - 'guide.harmony.arithmeticCaption', - 'White pieces 6, 9, 12 in a row in enemy territory form an arithmetic progression' - )} -

-
- - {/* Geometric Progression */} -
-

- {t('guide.harmony.geometric', 'Geometric Progression')} -

-

- {t('guide.harmony.geometricDesc', 'Middle value is geometric mean')} -

-

- {t('guide.harmony.geometricExample', 'Example: 4, 8, 16 (because 8² = 4×16)')} -

-
- -
-

- {t( - 'guide.harmony.geometricCaption', - 'White pieces 4, 8, 16 in a row in enemy territory form a geometric progression' - )} -

-
- - {/* Harmonic Progression */} -
-

- {t('guide.harmony.harmonic', 'Harmonic Progression')} -

-

- {t('guide.harmony.harmonicDesc', 'Special proportion (formula: 2AB = M(A+B))')} -

-

- {t('guide.harmony.harmonicExample', 'Example: 6, 8, 12 (because 2×6×12 = 8×(6+12))')} -

-
- -
-

- {t( - 'guide.harmony.harmonicCaption', - 'White pieces 6, 8, 12 in a row in enemy territory form a harmonic progression' - )} -

-
-
- -
-

- {t('guide.harmony.rulesTitle', '⚠️ Important Rules')} -

- -
-
- ) -} - -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 ( -
-

- {t('guide.victory.title', 'How to Win')} -

- -
-
-

- 👑 - {t('guide.victory.harmony', 'Victory #1: Harmony (Progression)')} -

-

- {t( - 'guide.victory.harmonyDesc', - "Form a mathematical progression with 3 pieces in enemy territory. If it survives your opponent's next turn, you win!" - )} -

- - {/* Visual example of winning harmony */} -
-

- {t('guide.victory.exampleTitle', 'Example: White Wins!')} -

-
- -
-

- {t( - 'guide.victory.exampleCaption', - 'White pieces 4, 8, 16 form a geometric progression in enemy territory. Black cannot break it - White wins!' - )} -

-
- -
-

- {t( - 'guide.victory.harmonyNote', - 'This is the primary victory condition in Rithmomachia' - )} -

-
-
- -
-

- 🚫 - {t('guide.victory.exhaustion', 'Victory #2: Exhaustion')} -

-

- {t( - 'guide.victory.exhaustionDesc', - 'If your opponent has no legal moves at the start of their turn, they lose.' - )} -

-
-
- -

- {t('guide.victory.strategyTitle', 'Quick Strategy Tips')} -

- -
- ) -} diff --git a/apps/web/src/arcade-games/rithmomachia/components/guide-sections/CaptureSection.tsx b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/CaptureSection.tsx new file mode 100644 index 00000000..5916b9e6 --- /dev/null +++ b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/CaptureSection.tsx @@ -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 ( +
+

+ {t('guide.capture.title', 'How to Capture')} +

+

+ {t( + 'guide.capture.description', + 'You can only capture an enemy piece if your piece value has a mathematical relation to theirs:' + )} +

+ +

+ {t('guide.capture.simpleTitle', 'Simple Relations (no helper needed)')} +

+ + {/* Equality */} +
+

+ {t('guide.capture.equality', 'Equal')} +

+

+ {t('guide.capture.equalityExample', 'Your 25 captures their 25')} +

+
+ +
+

+ {t( + 'guide.capture.equalityCaption', + 'White Circle (25) can capture Black Circle (25) by equality' + )} +

+
+ + {/* Multiple/Divisor */} +
+

+ {t('guide.capture.multiple', 'Multiple / Divisor')} +

+

+ {t('guide.capture.multipleExample', 'Your 64 captures their 16 (64 ÷ 16 = 4)')} +

+
+ +
+

+ {t( + 'guide.capture.multipleCaption', + 'White Square (64) can capture Black Triangle (16) because 64 ÷ 16 = 4' + )} +

+
+ +

+ {t('guide.capture.advancedTitle', 'Advanced Relations (need one helper piece)')} +

+ + {/* Sum */} +
+

+ {t('guide.capture.sum', 'Sum')} +

+

+ {t('guide.capture.sumExample', 'Your 9 + helper 16 = enemy 25')} +

+
+ +
+

+ {t( + 'guide.capture.sumCaption', + 'White Circle (9) can capture Black Circle (25) using helper Triangle (16): 9 + 16 = 25' + )} +

+
+ + {/* Difference */} +
+

+ {t('guide.capture.difference', 'Difference')} +

+

+ {t('guide.capture.differenceExample', 'Your 30 - helper 10 = enemy 20')} +

+
+ +
+

+ {t( + 'guide.capture.differenceCaption', + 'White Triangle (30) can capture Black Triangle (20) using helper Circle (10): 30 - 10 = 20' + )} +

+
+ + {/* Product */} +
+

+ {t('guide.capture.product', 'Product')} +

+

+ {t('guide.capture.productExample', 'Your 5 × helper 5 = enemy 25')} +

+
+ +
+

+ {t( + 'guide.capture.productCaption', + 'White Circle (5) can capture Black Circle (25) using helper Circle (5): 5 × 5 = 25' + )} +

+
+ +
+

+ {t('guide.capture.helpersTitle', '💡 What are helpers?')} +

+

+ {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.' + )} +

+
+
+ ) +} diff --git a/apps/web/src/arcade-games/rithmomachia/components/guide-sections/HarmonySection.tsx b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/HarmonySection.tsx new file mode 100644 index 00000000..81fda0d3 --- /dev/null +++ b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/HarmonySection.tsx @@ -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 ( +
+

+ {t('guide.harmony.title', 'Harmonies (Progressions)')} +

+

+ {t( + 'guide.harmony.description', + 'Get 3 of your pieces into enemy territory forming one of these progressions:' + )} +

+ +
+ {/* Arithmetic Progression */} +
+

+ {t('guide.harmony.arithmetic', 'Arithmetic Progression')} +

+

+ {t('guide.harmony.arithmeticDesc', 'Middle value is the average')} +

+

+ {t('guide.harmony.arithmeticExample', 'Example: 6, 9, 12 (because 9 = (6+12)/2)')} +

+
+ +
+

+ {t( + 'guide.harmony.arithmeticCaption', + 'White pieces 6, 9, 12 in a row in enemy territory form an arithmetic progression' + )} +

+
+ + {/* Geometric Progression */} +
+

+ {t('guide.harmony.geometric', 'Geometric Progression')} +

+

+ {t('guide.harmony.geometricDesc', 'Middle value is geometric mean')} +

+

+ {t('guide.harmony.geometricExample', 'Example: 4, 8, 16 (because 8² = 4×16)')} +

+
+ +
+

+ {t( + 'guide.harmony.geometricCaption', + 'White pieces 4, 8, 16 in a row in enemy territory form a geometric progression' + )} +

+
+ + {/* Harmonic Progression */} +
+

+ {t('guide.harmony.harmonic', 'Harmonic Progression')} +

+

+ {t('guide.harmony.harmonicDesc', 'Special proportion (formula: 2AB = M(A+B))')} +

+

+ {t('guide.harmony.harmonicExample', 'Example: 6, 8, 12 (because 2×6×12 = 8×(6+12))')} +

+
+ +
+

+ {t( + 'guide.harmony.harmonicCaption', + 'White pieces 6, 8, 12 in a row in enemy territory form a harmonic progression' + )} +

+
+
+ +
+

+ {t('guide.harmony.rulesTitle', '⚠️ Important Rules')} +

+ +
+
+ ) +} diff --git a/apps/web/src/arcade-games/rithmomachia/components/guide-sections/OverviewSection.tsx b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/OverviewSection.tsx new file mode 100644 index 00000000..8723ca77 --- /dev/null +++ b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/OverviewSection.tsx @@ -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 ( +
+

+ {t('guide.overview.goalTitle', 'Goal of the Game')} +

+

+ {t( + 'guide.overview.goal', + 'Arrange 3 of your pieces in enemy territory to form a mathematical progression, survive one opponent turn, and win.' + )} +

+ +

+ {t('guide.overview.boardTitle', 'The Board')} +

+ +
+ +
+ +

+ {t( + 'guide.overview.boardCaption', + 'The starting position - Black on the left, White on the right' + )} +

+ + + +

+ {t('guide.overview.howToPlayTitle', 'How to Play')} +

+
    +
  1. {t('guide.overview.step1', 'Move your pieces toward the center')}
  2. +
  3. {t('guide.overview.step2', 'Look for ways to capture using math')}
  4. +
  5. {t('guide.overview.step3', 'Push into enemy territory')}
  6. +
  7. {t('guide.overview.step4', 'Watch for chances to make a progression')}
  8. +
  9. {t('guide.overview.step5', 'Win by forming a progression that survives one turn!')}
  10. +
+
+ ) +} diff --git a/apps/web/src/arcade-games/rithmomachia/components/guide-sections/PiecesSection.tsx b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/PiecesSection.tsx new file mode 100644 index 00000000..31fa2b03 --- /dev/null +++ b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/PiecesSection.tsx @@ -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 ( +
+

+ {t('guide.pieces.title', 'Your Pieces (25 total)')} +

+

+ {t( + 'guide.pieces.description', + 'Each side has 25 pieces with different movement patterns. The shape tells you how it moves:' + )} +

+ +
+ {pieces.map((piece) => ( +
+
+
+ +
+
+

+ {piece.name} ({piece.count} per side) +

+

{piece.movement}

+
+
+ + {/* Example values */} +
+

+ {t('guide.pieces.exampleValues', 'Example values')}: +

+
+ {piece.exampleValues.map((value) => ( +
+ +
+ ))} +
+
+
+ ))} +
+ + {/* Pyramid section */} +
+

+ {t('guide.pieces.pyramidTitle', '⭐ Pyramids are special')} +

+

+ {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.' + )} +

+ +
+ {/* Black Pyramid */} +
+

+ {t('guide.pieces.blackPyramid', 'Black Pyramid')} +

+
+ +
+

+ {t('guide.pieces.pyramidValues', 'Values')}: 36, 25, 16, 4 +

+
+ + {/* White Pyramid */} +
+

+ {t('guide.pieces.whitePyramid', 'White Pyramid')} +

+
+ +
+

+ {t('guide.pieces.pyramidValues', 'Values')}: 64, 49, 36, 25 +

+
+
+
+
+ ) +} diff --git a/apps/web/src/arcade-games/rithmomachia/components/guide-sections/VictorySection.tsx b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/VictorySection.tsx new file mode 100644 index 00000000..4eba4b6b --- /dev/null +++ b/apps/web/src/arcade-games/rithmomachia/components/guide-sections/VictorySection.tsx @@ -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 ( +
+

+ {t('guide.victory.title', 'How to Win')} +

+ +
+
+

+ 👑 + {t('guide.victory.harmony', 'Victory #1: Harmony (Progression)')} +

+

+ {t( + 'guide.victory.harmonyDesc', + "Form a mathematical progression with 3 pieces in enemy territory. If it survives your opponent's next turn, you win!" + )} +

+ + {/* Visual example of winning harmony */} +
+

+ {t('guide.victory.exampleTitle', 'Example: White Wins!')} +

+
+ +
+

+ {t( + 'guide.victory.exampleCaption', + 'White pieces 4, 8, 16 form a geometric progression in enemy territory. Black cannot break it - White wins!' + )} +

+
+ +
+

+ {t( + 'guide.victory.harmonyNote', + 'This is the primary victory condition in Rithmomachia' + )} +

+
+
+ +
+

+ 🚫 + {t('guide.victory.exhaustion', 'Victory #2: Exhaustion')} +

+

+ {t( + 'guide.victory.exhaustionDesc', + 'If your opponent has no legal moves at the start of their turn, they lose.' + )} +

+
+
+ +

+ {t('guide.victory.strategyTitle', 'Quick Strategy Tips')} +

+ +
+ ) +}