This file is a merged representation of the entire codebase, combined into a single document by Repomix. The content has been processed where empty lines have been removed, content has been compressed (code blocks are separated by ⋮---- delimiter). This section contains a summary of this file. This file contains a packed representation of the entire repository's contents. It is designed to be easily consumable by AI systems for analysis, code review, or other automated processes. The content is organized as follows: 1. This summary section 2. Repository information 3. Directory structure 4. Repository files (if enabled) 5. Multiple file entries, each consisting of: - File path as an attribute - Full contents of the file - This file should be treated as read-only. Any changes should be made to the original repository files, not this packed version. - When processing this file, use the file path to distinguish between different files in the repository. - Be aware that this file may contain sensitive information. Handle it with the same level of security as you would the original repository. - Some files may have been excluded based on .gitignore rules and Repomix's configuration - Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files - Files matching patterns in .gitignore are excluded - Files matching default ignore patterns are excluded - Empty lines have been removed from all files - Content has been compressed - code blocks are separated by ⋮---- delimiter - Files are sorted by Git change count (files with more changes are at the bottom) _site/ ts/ assets/ hierarchy.js highlight.css icons.js icons.svg main.js navigation.js search.js style.css .claude/ commands/ desktop-ux-review.md mobile-ux-review.md rules/ frontend.md go.md skills/ doc-drift-check/ SKILL.md make-issue/ SKILL.md make-issue-cli/ SKILL.md make-issue-web/ SKILL.md settings.json .gemini/ styleguide.md .github/ workflows/ ci.yml claude-review.yml cloudflare-workers-build.yml codeql-analysis.yml deploy-cloudflare.yml deploy-pages.yml release-tag.yml api/ embed.go openapi.yaml cmd/ server/ main.go trumpcards/ completion.go main.go workers/ casino/ main.go classic/ main.go solo/ main.go docs/ adr/ 0001-clean-architecture.md 0002-presenter-pattern.md 0003-golang-standards-layout.md 0004-react-frontend.md 0005-stateless-rest-api.md 0006-openapi-specification.md 0007-tailwind-css.md 0008-biome-linter.md 0009-tdd-and-coverage.md 0010-tanstack-react-query.md 0011-i18n-react-i18next.md 0012-playwright-e2e.md 0013-docker-distroless.md 0015-accessibility-wcag.md 0016-production-middleware.md 0019-ci-cd-pipeline.md 0021-bun-package-manager.md 0022-automated-quality-gates.md 0023-api-documentation.md 0026-relax-coverage-target.md 0027-cloudflare-workers-wasm.md 0028-kv-session-persistence.md 0029-design-system.md README.md design/ backend.md frontend.md manual/ cui/ baccarat.md blackjack.md bridge.md canasta.md clocksolitaire.md crazyeights.md cribbage.md daifugo.md deuceswild.md doubt.md euchre.md freecell.md ginrummy.md gofish.md golf.md hearts.md holdem.md indianpoker.md jokerpoker.md klondike.md memory.md napoleon.md ohhell.md oldmaid.md omaha.md pigtail.md pineapple.md pinochle.md poker.md pyramid.md sevencardstud.md sevens.md shortdeck.md spades.md speed.md spider.md threecard.md tripeaks.md videopoker.md web/ baccarat.md blackjack.md bridge.md canasta.md clocksolitaire.md crazyeights.md cribbage.md daifugo.md deuceswild.md doubt.md euchre.md freecell.md ginrummy.md gofish.md golf.md hearts.md holdem.md indianpoker.md jokerpoker.md klondike.md memory.md napoleon.md ohhell.md oldmaid.md omaha.md pigtail.md pineapple.md pinochle.md poker.md pyramid.md sevencardstud.md sevens.md shortdeck.md spades.md speed.md spider.md threecard.md tripeaks.md videopoker.md cui_template.md web_template.md architecture.md games.md new-game-checklist.md frontend/ public/ sounds/ card-deal.ogg card-flip.ogg card-place.ogg card-select.ogg chip-click.ogg error-buzz.ogg loss-thud.ogg shuffle.ogg turn-tick.ogg win-fanfare.ogg vite.svg src/ api/ gameApi.ts assets/ react.svg components/ __snapshots__/ StatusBadge.test.tsx.snap blackjack/ BjActionPhaseControls.tsx BjBetPhaseControls.tsx bjConstants.ts BjEarlySurrenderPhaseControls.tsx BjEndPhaseControls.tsx BjInsurancePhaseControls.tsx HandStatusBadges.tsx cli/ CliTerminal.tsx CliToggle.tsx common/ SettingsPanel.tsx daifugo/ DaifugoCpuArea.tsx DaifugoExchangeLog.tsx DaifugoHumanArea.tsx DaifugoRulesBadges.tsx DaifugoSettingsPanel.tsx doubt/ CountdownBar.tsx DoubtCpuArea.tsx DoubtHandCard.tsx gofish/ GoFishBooksDisplay.tsx GoFishPlayerArea.tsx GoFishSettingsDialog.tsx hint/ HintPulse.tsx HintTooltip.tsx motion/ AnimatedCard.tsx AnimatedCardBack.tsx AnimatedPile.tsx LossFeedback.tsx WinCelebration.tsx oldmaid/ OldMaidDiscardedArea.tsx OldMaidDrawHistory.tsx OldMaidPlayerArea.tsx OldMaidSettingsDialog.tsx sevens/ SevensBoard.tsx SevensCpuArea.tsx SevensHumanArea.tsx skeleton/ BaccaratSkeleton.tsx BlackJackSkeleton.tsx BridgeSkeleton.tsx CrazyEightsSkeleton.tsx CribbageSkeleton.tsx DaifugoSkeleton.tsx DoubtSkeleton.tsx EuchreSkeleton.tsx FreeCellSkeleton.tsx GameSkeleton.tsx GinRummySkeleton.tsx GoFishSkeleton.tsx GolfSkeleton.tsx HeartsSkeleton.tsx HoldemSkeleton.tsx KlondikeSkeleton.tsx MemorySkeleton.tsx NapoleonSkeleton.tsx OhHellSkeleton.tsx OldMaidSkeleton.tsx OmahaSkeleton.tsx PigsTailSkeleton.tsx PokerSkeleton.tsx PyramidSkeleton.tsx SevensSkeleton.tsx ShortDeckSkeleton.tsx SkeletonBar.tsx SkeletonCard.tsx SkeletonGrid.tsx SkeletonHand.tsx SpadesSkeleton.tsx SpeedSkeleton.tsx SpiderSkeleton.tsx ThreeCardSkeleton.tsx TriPeaksSkeleton.tsx tutorial/ TutorialButton.tsx TutorialOverlay.tsx TutorialProgressPanel.tsx TutorialSuggestDialog.tsx TutorialTooltip.tsx TutorialWrapper.tsx ActionLogPanel.tsx ActionLogSection.tsx BettingControls.tsx CardImage.tsx ConfirmDialog.tsx CpuAccordion.tsx CpuActionLog.tsx CpuActionToast.tsx CpuPlayerCard.tsx CpuTurnArea.tsx DesktopSidebar.tsx EquityDisplay.tsx ErrorAlert.tsx ErrorBoundary.tsx GameFooter.tsx GameMessageBox.tsx GamePageHeading.tsx GamePageShell.tsx GameResetDialog.tsx LandscapeBanner.tsx ManualButton.tsx ManualModal.tsx MermaidBlock.tsx MetaAiIndicator.tsx MetaAiToast.tsx MobileHandGrid.tsx NavBar.tsx PhaseIndicator.tsx PlayerHandSection.tsx PokerTableLayout.tsx RoundResults.tsx ScrollFadeHint.tsx SkipNavLink.tsx SoundToggle.tsx StatusBadge.tsx VideoPokerGameContent.tsx constants/ gameRoutes.ts manualTexts.ts messages.ts site.ts tutorialKeys.ts hooks/ gameReplay.ts keyboardNavUtils.ts useActionKeyboardNav.ts useActionLog.ts useAutoCompleteState.ts useBridgeGame.ts useCanastaGame.ts useCardDimensions.ts useCardKeyboardNav.ts useCardSelection.ts useCliGame.ts useCliMode.ts useConfirmDialog.ts useCrazyEightsGame.ts useCribbageGame.ts useDaifugoGame.ts useDealSequence.ts useDoubtGame.ts useEuchreGame.ts useFavoriteGames.ts useFirstVisit.ts useFreeCellGame.ts useGameApi.ts useGameConfig.ts useGameHint.ts useGamePageSetup.ts useGinRummyGame.ts useGoFishGame.ts useGolfGame.ts useHaptics.ts useHeartsGame.ts useKlondikeGame.ts useKlondikeTimer.ts useLocalStorageToggle.ts useMemoryGame.ts useNapoleonGame.ts useOhHellGame.ts useOldMaidGame.ts usePhaseNames.ts usePinochleGame.ts usePokerGame.ts useProfilePersistence.ts usePyramidGame.ts useRecentGames.ts useReducedMotion.ts useSevensGame.ts useSpadesGame.ts useSpeedGame.ts useSpiderGame.ts useTrickGameBase.ts useTriPeaksGame.ts useTutorial.ts useTutorialProgress.ts i18n/ locales/ en/ baccarat.json blackjack.json bridge.json canasta.json clocksolitaire.json common.json crazyeights.json cribbage.json daifugo.json deuceswild.json doubt.json euchre.json freecell.json ginrummy.json gofish.json golf.json hearts.json holdem.json indianpoker.json jokerpoker.json klondike.json memory.json napoleon.json ohhell.json oldmaid.json omaha.json pigtail.json pineapple.json pinochle.json poker.json pyramid.json sevencardstud.json sevens.json shortdeck.json spades.json speed.json spider.json threecard.json tripeaks.json tutorial.json videopoker.json ja/ baccarat.json blackjack.json bridge.json canasta.json clocksolitaire.json common.json crazyeights.json cribbage.json daifugo.json deuceswild.json doubt.json euchre.json freecell.json ginrummy.json gofish.json golf.json hearts.json holdem.json indianpoker.json jokerpoker.json klondike.json memory.json napoleon.json ohhell.json oldmaid.json omaha.json pigtail.json pineapple.json pinochle.json poker.json pyramid.json sevencardstud.json sevens.json shortdeck.json spades.json speed.json spider.json threecard.json tripeaks.json tutorial.json videopoker.json buildResources.ts index.ts pages/ BaccaratPage.tsx BlackJackPage.tsx BridgePage.tsx CanastaPage.tsx ClockSolitairePage.tsx CrazyEightsPage.tsx CribbagePage.tsx DaifugoPage.tsx DeucesWildPage.tsx DoubtPage.tsx EuchrePage.tsx FreeCellPage.tsx GinRummyPage.tsx GoFishPage.tsx GolfPage.tsx HeartsPage.tsx HoldemPage.tsx IndianPokerPage.tsx JokerPokerPage.tsx KlondikePage.tsx MemoryPage.tsx NapoleonPage.tsx OhHellPage.tsx OldMaidPage.tsx OmahaPage.tsx PigsTailPage.tsx PineapplePage.tsx PinochlePage.tsx PokerPage.tsx PyramidPage.tsx SevenCardStudPage.tsx SevensPage.tsx ShortDeckPage.tsx SpadesPage.tsx SpeedPage.tsx SpiderPage.tsx ThreeCardPage.tsx TriPeaksPage.tsx VideoPokerPage.tsx providers/ QueryProvider.tsx SoundProvider.tsx TutorialProvider.tsx styles/ buttonStyles.ts cardStyles.ts gameConstants.ts gameStyles.ts gameTheme.ts motionPresets.ts test/ renderWithProviders.tsx setup.ts stateFactories.ts types/ card.ts hint.ts phases.ts tutorial.ts utils/ cli/ commands/ baccaratCommands.ts blackjackCommands.ts bridgeCommands.ts canastaCommands.ts crazyeightsCommands.ts cribbageCommands.ts daifugoCommands.ts deuceswildCommands.ts doubtCommands.ts euchreCommands.ts freecellCommands.ts ginrummyCommands.ts gofishCommands.ts golfCommands.ts heartsCommands.ts holdemCommands.ts indianpokerCommands.ts jokerpokerCommands.ts klondikeCommands.ts memoryCommands.ts napoleonCommands.ts ohhellCommands.ts oldmaidCommands.ts omahaCommands.ts pineappleCommands.ts pinochleCommands.ts pokerCommands.ts pyramidCommands.ts sevensCommands.ts sharedBettingCommands.ts sharedTrickCommands.ts sharedVideoPokerCommands.ts shortdeckCommands.ts spadesCommands.ts speedCommands.ts spiderCommands.ts threecardCommands.ts tripeaksCommands.ts videopokerCommands.ts formatters/ baccaratFormatter.ts blackjackFormatter.ts bridgeFormatter.ts canastaFormatter.ts crazyeightsFormatter.ts cribbageFormatter.ts daifugoFormatter.ts deuceswildFormatter.ts doubtFormatter.ts euchreFormatter.ts freecellFormatter.ts genericFormatter.ts ginrummyFormatter.ts gofishFormatter.ts golfFormatter.ts heartsFormatter.ts holdemFormatter.ts indianpokerFormatter.ts jokerpokerFormatter.ts klondikeFormatter.ts memoryFormatter.ts napoleonFormatter.ts ohhellFormatter.ts oldmaidFormatter.ts omahaFormatter.ts pineappleFormatter.ts pinochleFormatter.ts pokerFormatter.ts pyramidFormatter.ts sevensFormatter.ts shortdeckFormatter.ts spadesFormatter.ts speedFormatter.ts spiderFormatter.ts threecardFormatter.ts tripeaksFormatter.ts videopokerFormatter.ts commandParserBase.ts formatterBase.ts types.ts hints/ baccaratHint.ts blackjackHint.ts crazyeightsHint.ts cribbageHint.ts daifugoHint.ts deuceswildHint.ts doubtHint.ts euchreHint.ts freecellHint.ts ginrummyHint.ts heartsHint.ts holdemBaseHint.ts holdemHint.ts indianpokerHint.ts jokerpokerHint.ts klondikeHint.ts memoryHint.ts napoleonHint.ts ohhellHint.ts oldmaidHint.ts omahaHint.ts pineappleHint.ts pokerHint.ts pyramidHint.ts sevensHint.ts shortdeckHint.ts spadesHint.ts speedHint.ts spiderHint.ts threecardHint.ts tripeaksHint.ts videoPokerBaseHint.ts videopokerHint.ts arrayUtils.ts cardAlt.ts cardUtils.ts dom.ts metaAiAdaptation.ts playerUtils.ts replayBuilder.ts sevensUtils.ts App.tsx index.css main.tsx .gitignore AGENTS.md biome.json CLAUDE.md GEMINI.md package.json playwright.config.ts README.md tsconfig.app.json tsconfig.json tsconfig.node.json typedoc.json vite.config.ts internal/ adapter/ controller/ cuimsg/ messages.go cuiutil/ parse.go prompt.go suggest.go usecase/ BaccaratInteractor_mock.go BlackJackInteractor_mock.go BridgeInteractor_mock.go CanastaInteractor_mock.go ClockSolitaireInteractor_mock.go CrazyEightsInteractor_mock.go CribbageInteractor_mock.go DaifugoInteractor_mock.go DoubtInteractor_mock.go EuchreInteractor_mock.go FreeCellInteractor_mock.go GinRummyInteractor_mock.go GoFishInteractor_mock.go GolfInteractor_mock.go HeartsInteractor_mock.go HoldemInteractor_mock.go IndianPokerInteractor_mock.go KlondikeInteractor_mock.go MemoryInteractor_mock.go NapoleonInteractor_mock.go OhHellInteractor_mock.go OldMaidInteractor_mock.go OmahaInteractor_mock.go PigsTailInteractor_mock.go PineappleInteractor_mock.go PinochleInteractor_mock.go PokerInteractor_mock.go PyramidInteractor_mock.go SevenCardStudInteractor_mock.go SevensInteractor_mock.go ShortDeckInteractor_mock.go SpadesInteractor_mock.go SpeedInteractor_mock.go SpiderInteractor_mock.go ThreeCardInteractor_mock.go TriPeaksInteractor_mock.go VideoPokerInteractor_mock.go webutil/ clamp.go action_log_web_output.go BaccaratCuiController.go BaccaratWebController.go base_controller.go BlackJackCuiController.go BlackJackWebController.go BridgeCuiController.go BridgeWebController.go CanastaCuiController.go CanastaWebController.go ClockSolitaireCuiController.go ClockSolitaireWebController.go CrazyEightsCuiController.go CrazyEightsWebController.go CribbageCuiController.go CribbageWebController.go cui_controller_helper.go DaifugoCuiController.go DaifugoWebController.go DoubtCuiController.go DoubtWebController.go EuchreCuiController.go EuchreWebController.go FreeCellCuiController.go FreeCellWebController.go game_web_controller.go GinRummyCuiController.go GinRummyWebController.go GoFishCuiController.go GoFishWebController.go GolfCuiController.go GolfWebController.go HeartsCuiController.go HeartsWebController.go HoldemCuiController.go HoldemWebController.go IndianPokerCuiController.go IndianPokerWebController.go KlondikeCuiController.go KlondikeWebController.go kv_session_provider.go MemoryCuiController.go MemoryWebController.go NapoleonCuiController.go NapoleonWebController.go OhHellCuiController.go OhHellWebController.go OldMaidCuiController.go OldMaidWebController.go OmahaCuiController.go OmahaWebController.go PigsTailCuiController.go PigsTailWebController.go PineappleCuiController.go PineappleWebController.go PinochleCuiController.go PinochleWebController.go PokerCuiController.go PokerWebController.go PyramidCuiController.go PyramidWebController.go session_provider.go session_store.go SevenCardStudCuiController.go SevenCardStudWebController.go SevensCuiController.go SevensWebController.go ShortDeckCuiController.go ShortDeckWebController.go SpadesCuiController.go SpadesWebController.go SpeedCuiController.go SpeedWebController.go SpiderCuiController.go SpiderWebController.go ThreeCardCuiController.go ThreeCardWebController.go TriPeaksCuiController.go TriPeaksWebController.go VideoPokerCuiController.go VideoPokerWebController.go web_controller_helper.go web_dispatch_helper.go web_output_base.go web_output_card.go presenter/ action_log_helper.go BaccaratCuiPresenter.go BaccaratWebPresenter.go BlackJackCuiPresenter.go BlackJackWebPresenter.go BridgeCuiPresenter.go BridgeWebPresenter.go CanastaCuiPresenter.go CanastaWebPresenter.go card_design_helper.go card_output_helper.go ClockSolitaireCuiPresenter.go ClockSolitaireWebPresenter.go CrazyEightsCuiPresenter.go CrazyEightsWebPresenter.go CribbageCuiPresenter.go CribbageWebPresenter.go cui_card_helper.go cui_output_helper.go daifugo_presenter_helper.go DaifugoCuiPresenter.go DaifugoWebPresenter.go DoubtCuiPresenter.go DoubtWebPresenter.go error_response.go EuchreCuiPresenter.go EuchreWebPresenter.go FreeCellCuiPresenter.go FreeCellWebPresenter.go GinRummyCuiPresenter.go GinRummyWebPresenter.go GoFishCuiPresenter.go GoFishWebPresenter.go GolfCuiPresenter.go GolfWebPresenter.go HeartsCuiPresenter.go HeartsWebPresenter.go HoldemCuiPresenter.go HoldemWebPresenter.go IndianPokerCuiPresenter.go IndianPokerWebPresenter.go json_marshal.go KlondikeCuiPresenter.go KlondikeWebPresenter.go MemoryCuiPresenter.go MemoryWebPresenter.go NapoleonCuiPresenter.go NapoleonWebPresenter.go OhHellCuiPresenter.go OhHellWebPresenter.go OldMaidCuiPresenter.go OldMaidWebPresenter.go OmahaCuiPresenter.go OmahaWebPresenter.go PigsTailCuiPresenter.go PigsTailWebPresenter.go PineappleCuiPresenter.go PineappleWebPresenter.go PinochleCuiPresenter.go PinochleWebPresenter.go PokerCuiPresenter.go PokerWebPresenter.go PyramidCuiPresenter.go PyramidWebPresenter.go SevenCardStudCuiPresenter.go SevenCardStudWebPresenter.go SevensCuiPresenter.go SevensWebPresenter.go ShortDeckCuiPresenter.go ShortDeckWebPresenter.go SpadesCuiPresenter.go SpadesWebPresenter.go SpeedCuiPresenter.go SpeedWebPresenter.go SpiderCuiPresenter.go SpiderWebPresenter.go ThreeCardCuiPresenter.go ThreeCardWebPresenter.go TriPeaksCuiPresenter.go TriPeaksWebPresenter.go VideoPokerCuiPresenter.go VideoPokerWebPresenter.go web_presenter_helper.go color/ color.go domain/ interfaces/ baccarat_mock.go baccarat.go base.go blackjack_mock.go blackjack.go bridge_mock.go bridge.go canasta_mock.go canasta.go clocksolitaire_mock.go clocksolitaire.go crazyeights_mock.go crazyeights.go cribbage_mock.go cribbage.go daifugo_mock.go daifugo.go doubt_mock.go doubt.go euchre_mock.go euchre.go freecell_mock.go freecell.go ginrummy_mock.go ginrummy.go gofish_mock.go gofish.go golf_mock.go golf.go hearts_mock.go hearts.go holdem_mock.go holdem.go indianpoker_mock.go indianpoker.go klondike_mock.go klondike.go memory_mock.go memory.go napoleon_mock.go napoleon.go ohHell_mock.go ohHell.go oldmaid_mock.go oldmaid.go omaha_mock.go omaha.go pigstail_mock.go pigstail.go pineapple_mock.go pineapple.go pinochle_mock.go pinochle.go poker_mock.go poker.go pyramid_mock.go pyramid.go sevencardstud_mock.go sevencardstud.go sevens_mock.go sevens.go shortdeck_mock.go shortdeck.go spades_mock.go spades.go speed_mock.go speed.go spider_mock.go spider.go threecard_mock.go threecard.go tournament_action.go tripeaks_mock.go tripeaks.go videopoker_mock.go videopoker.go ActionLog.go Baccarat.go BaccaratSideBet.go betting_player_base.go betting.go BettingHumanProfile.go BlackJack_testhelpers.go BlackJack.go BlackJackBasicStrategy.go BlackJackConfig.go BlackJackCountingStrategy.go BlackJackCPU.go BlackJackCpuSeat.go BlackJackEval.go BlackJackHand.go BlackJackPlayer.go BlackJackSideBet.go Bridge.go BridgeConfig.go BridgePlayer.go Canasta.go CanastaConfig.go CanastaPlayer.go Card.go ChipHolder.go ClockSolitaire_testhelpers.go ClockSolitaire.go CrazyEights.go CrazyEightsConfig.go CrazyEightsPlayer.go cribbage_scoring.go Cribbage.go CribbageConfig.go CribbagePlayer.go daifugo_solver_movegen.go daifugo_solver_unbeat.go daifugo_solver.go Daifugo_testhelpers.go Daifugo.go DaifugoConfig.go DaifugoCPU.go DaifugoEval.go DaifugoPlayer.go DaifugoRules.go deal_helper.go Doubt_testhelpers.go Doubt.go DoubtConfig.go DoubtHumanProfile.go DoubtPlayer.go equity_engine.go equity_simulations_wasm.go equity_simulations.go errors.go Euchre.go EuchreConfig.go EuchrePlayer.go freecell_solver.go FreeCell.go GamePlayer.go GinRummy.go GinRummyConfig.go GinRummyPlayer.go GoFish.go GoFishConfig.go GoFishPlayer.go Golf.go hand_eval_wild.go hand_eval.go Hearts.go HeartsConfig.go HeartsPlayer.go hesitation.go Holdem_testhelpers.go Holdem.go HoldemConfig.go HoldemCPU.go HoldemEquity.go HoldemEval.go HoldemPlayer_testhelpers.go HoldemPlayer.go HoldemRebuy.go IndianPoker.go IndianPokerConfig.go IndianPokerHumanProfile.go IndianPokerPlayer.go kicker.go klondike_solver.go Klondike.go memory_decay.go memory_manager.go Memory.go MemoryConfig.go MemoryPlayer.go Napoleon.go NapoleonConfig.go NapoleonPlayer.go OhHell.go OhHellConfig.go OhHellPlayer.go OldMaid_testhelpers.go OldMaid.go OldMaidConfig.go OldMaidHumanProfile.go OldMaidPlayer.go Omaha_testhelpers.go Omaha.go OmahaConfig.go OmahaEquity.go OmahaPlayer_testhelpers.go OmahaPlayer.go PigsTail_testhelpers.go PigsTail.go PigsTailConfig.go PigsTailPlayer.go Pineapple.go PineappleConfig.go PineappleCPU.go PineapplePlayer.go Pinochle.go PinochleConfig.go PinochlePlayer.go play_style_helper.go player_helpers.go Player.go Poker_testhelpers.go Poker.go PokerConfig.go PokerOdds.go PokerPlayer.go Pyramid.go RoundScoreHolder.go SevenCardStud_testhelpers.go SevenCardStud.go SevenCardStudConfig.go SevenCardStudCPU.go SevenCardStudPlayer_testhelpers.go SevenCardStudPlayer.go SevenCardStudRebuy.go Sevens_testhelpers.go Sevens.go SevensConfig.go SevensPlayer.go shortdeck_hand_eval.go ShortDeck_testhelpers.go ShortDeck.go ShortDeckConfig.go ShortDeckCPU.go ShortDeckEquity.go ShortDeckPlayer_testhelpers.go ShortDeckPlayer.go Spades.go SpadesConfig.go SpadesPlayer.go Speed.go SpeedConfig.go SpeedPlayer.go Spider.go SpiderConfig.go ThreeCard.go TrickHolder.go TriPeaks.go TrumpCards.go validation.go VideoPoker.go VideoPokerVariant.go i18n/ locales/ en/ baccarat.json blackjack.json clocksolitaire.json common.json crazyeights.json cribbage.json daifugo.json deuceswild.json doubt.json euchre.json freecell.json ginrummy.json golf.json hearts.json holdem.json indianpoker.json jokerpoker.json klondike.json memory.json napoleon.json ohhell.json oldmaid.json omaha.json pigtail.json pineapple.json pinochle.json poker.json pyramid.json sevencardstud.json sevens.json shortdeck.json spades.json speed.json spider.json threecard.json tripeaks.json videopoker.json ja/ baccarat.json blackjack.json clocksolitaire.json common.json crazyeights.json cribbage.json daifugo.json deuceswild.json doubt.json euchre.json freecell.json ginrummy.json golf.json hearts.json holdem.json indianpoker.json jokerpoker.json klondike.json memory.json napoleon.json ohhell.json oldmaid.json omaha.json pigtail.json pineapple.json pinochle.json poker.json pyramid.json sevencardstud.json sevens.json shortdeck.json spades.json speed.json spider.json threecard.json tripeaks.json videopoker.json i18n.go infrastructure/ cors/ cors.go ui/ BaccaratCui.go BlackJackCui.go BridgeCui.go CanastaCui.go ClockSolitaireCui.go CrazyEightsCui.go CribbageCui.go cui_runner.go DaifugoCui.go DeucesWildCui.go DoubtCui.go EuchreCui.go FreeCellCui.go GameManager.go generic_cui_game.go GinRummyCui.go GoFishCui.go GolfCui.go HeartsCui.go HoldemCui.go IndianPokerCui.go input.go JokerPokerCui.go KlondikeCui.go MemoryCui.go NapoleonCui.go OhHellCui.go OldMaidCui.go OmahaCui.go PigsTailCui.go PineappleCui.go PinochleCui.go PokerCui.go PyramidCui.go SevenCardStudCui.go SevensCui.go ShortDeckCui.go SpadesCui.go SpeedCui.go SpiderCui.go ThreeCardCui.go TriPeaksCui.go VideoPokerCui.go update/ Updater.go web/ swagger.go TrumpCardsWeb.go worker/ register.go logging.go usecase/ presenter/ BaccaratPresenter_mock.go BaccaratPresenter.go BlackJackPresenter_mock.go BlackJackPresenter.go BridgePresenter_mock.go BridgePresenter.go CanastaPresenter_mock.go CanastaPresenter.go ClockSolitairePresenter_mock.go ClockSolitairePresenter.go CrazyEightsPresenter_mock.go CrazyEightsPresenter.go CribbagePresenter_mock.go CribbagePresenter.go DaifugoPresenter_mock.go DaifugoPresenter.go DoubtPresenter_mock.go DoubtPresenter.go EuchrePresenter_mock.go EuchrePresenter.go FreeCellPresenter_mock.go FreeCellPresenter.go GinRummyPresenter_mock.go GinRummyPresenter.go GoFishPresenter_mock.go GoFishPresenter.go GolfPresenter_mock.go GolfPresenter.go HeartsPresenter_mock.go HeartsPresenter.go HoldemPresenter_mock.go HoldemPresenter.go IndianPokerPresenter_mock.go IndianPokerPresenter.go KlondikePresenter_mock.go KlondikePresenter.go MemoryPresenter_mock.go MemoryPresenter.go NapoleonPresenter_mock.go NapoleonPresenter.go OhHellPresenter_mock.go OhHellPresenter.go OldMaidPresenter_mock.go OldMaidPresenter.go OmahaPresenter_mock.go OmahaPresenter.go PigsTailPresenter_mock.go PigsTailPresenter.go PineapplePresenter_mock.go PineapplePresenter.go PinochlePresenter_mock.go PinochlePresenter.go PokerPresenter_mock.go PokerPresenter.go presenter_base_mock.go presenter_base.go PyramidPresenter_mock.go PyramidPresenter.go SevenCardStudPresenter_mock.go SevenCardStudPresenter.go SevensPresenter_mock.go SevensPresenter.go ShortDeckPresenter_mock.go ShortDeckPresenter.go SpadesPresenter_mock.go SpadesPresenter.go SpeedPresenter_mock.go SpeedPresenter.go SpiderPresenter_mock.go SpiderPresenter.go ThreeCardPresenter_mock.go ThreeCardPresenter.go TriPeaksPresenter_mock.go TriPeaksPresenter.go VideoPokerPresenter_mock.go VideoPokerPresenter.go BaccaratInteractor.go BlackJackInteractor.go BridgeInteractor.go CanastaInteractor.go ClockSolitaireInteractor.go CrazyEightsInteractor.go CribbageInteractor.go DaifugoInteractor.go DoubtInteractor.go EuchreInteractor.go FreeCellInteractor.go GinRummyInteractor.go GoFishInteractor.go GolfInteractor.go HeartsInteractor.go HoldemInteractor.go IndianPokerInteractor.go interactor_base.go interactor_guard.go interactor_helper.go interactor_restore.go interactor_trick.go KlondikeInteractor.go MemoryInteractor.go NapoleonInteractor.go OhHellInteractor.go OldMaidInteractor.go OmahaInteractor.go PigsTailInteractor.go PineappleInteractor.go PinochleInteractor.go PokerInteractor.go PyramidInteractor.go SevenCardStudInteractor.go SevensInteractor.go ShortDeckInteractor.go SpadesInteractor.go SpeedInteractor.go SpiderInteractor.go ThreeCardInteractor.go tournament_interactor.go TriPeaksInteractor.go validate.go VideoPokerInteractor.go AGENTS.md CLAUDE.md GEMINI.md public/ images/ c01.png c02.png c03.png c04.png c05.png c06.png c07.png c08.png c09.png c10.png c11.png c12.png c13.png d01.png d02.png d03.png d04.png d05.png d06.png d07.png d08.png d09.png d10.png d11.png d12.png d13.png favicon.ico h01.png h02.png h03.png h04.png h05.png h06.png h07.png h08.png h09.png h10.png h11.png h12.png h13.png icon-sm.png s01.png s02.png s03.png s04.png s05.png s06.png s07.png s08.png s09.png s10.png s11.png s12.png s13.png x01.png x02.png z01.png z02.png sounds/ card-deal.ogg card-flip.ogg card-select.ogg README.md win.ogg workers/ casino/ wrangler.toml classic/ wrangler.toml solo/ wrangler.toml .dockerignore .gitignore .golangci.yml .goreleaser.yaml .repomixignore AGENTS.md CLAUDE.md codecov.yml CONTRIBUTING.md DESIGN.md Dockerfile GEMINI.md go.mod LICENSE Makefile README.md SECURITY.md This section contains the contents of the repository's files. :root { @media (prefers-color-scheme: light) { :root { @media (prefers-color-scheme: dark) { :root { :root[data-theme='light'] { :root[data-theme='dark'] { pre, code { background: var(--code-background); } function addIcons() function updateUseElements() MMNEPVFCICPMFPCPTTAAATR "use strict";(()=> `,e)},t.Pipeline.load=function(e) `,this.app.updateIndexVisibility()}fromLocalStorage() $ /*! Bundled license information: lunr/lunr.js: (** * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 * Copyright (C) 2020 Oliver Nightingale * @license MIT *) (*! * lunr.utils * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.Set * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.tokenizer * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.Pipeline * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.Vector * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.stemmer * Copyright (C) 2020 Oliver Nightingale * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt *) (*! * lunr.stopWordFilter * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.trimmer * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.TokenSet * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.Index * Copyright (C) 2020 Oliver Nightingale *) (*! * lunr.Builder * Copyright (C) 2020 Oliver Nightingale *) */ @layer typedoc { ⋮---- :root { ⋮---- /* 0rem For mobile; unit is required for calculation in `calc` */ ⋮---- /* Light */ ⋮---- /* Not to be confused with [:active](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) */ ⋮---- /* type literal not included as links will never be generated to it */ ⋮---- /* reference not included as links will be colored with the kind that it points to */ ⋮---- /* Dark */ ⋮---- :root[data-theme="light"] { :root[data-theme="dark"] { html { *:focus-visible, .always-visible, h1, h1 { h2 { h3 { h4 { h5 { h6 { dl, dd { .container { /* Footer */ footer { footer > p { .container-main { ⋮---- /* toolbar, footer, margin */ ⋮---- body { a { a:hover { a.external[target="_blank"] { a.tsd-anchor-link { :target { code, pre { pre code { pre > button { pre:hover > button, blockquote { img { * { *::-webkit-scrollbar { *::-webkit-scrollbar-track { *::-webkit-scrollbar-thumb { dialog { dialog::backdrop { #tsd-overlay { #tsd-overlay.closing { .tsd-typography { .tsd-typography ul { .tsd-typography .tsd-index-panel h3, .tsd-typography h5, .tsd-typography p, .tsd-typography table { .tsd-typography td, .tsd-typography thead, .tsd-alert { .tsd-alert blockquote > :last-child, .tsd-alert-title { .tsd-alert-title span { .tsd-alert-note { .tsd-alert-tip { .tsd-alert-important { .tsd-alert-warning { .tsd-alert-caution { .tsd-breadcrumb { .tsd-breadcrumb a { .tsd-breadcrumb a:hover { .tsd-breadcrumb li { .tsd-breadcrumb li:after { .tsd-comment-tags { dl.tsd-comment-tag-group { dl.tsd-comment-tag-group dt { dl.tsd-comment-tag-group dd { code.tsd-tag { h1 code.tsd-tag:first-of-type { dl.tsd-comment-tag-group dd:before, dl.tsd-comment-tag-group dd pre, dl.tsd-comment-tag-group p { .tsd-panel.tsd-comment .lead { .tsd-panel.tsd-comment .lead:last-child { .tsd-filter-visibility h4 { .tsd-filter-item:not(:last-child) { .tsd-filter-input { .tsd-filter-input input[type="checkbox"] { .tsd-filter-input input[type="checkbox"]:disabled { .tsd-filter-input svg { ⋮---- /* Leaving this at full opacity breaks event listeners on Firefox. Don't remove unless you know what you're doing. */ ⋮---- .tsd-filter-input input[type="checkbox"]:focus-visible + svg { .tsd-checkbox-background { input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { .settings-label { .tsd-filter-visibility .settings-label { .tsd-theme-toggle .settings-label { .tsd-hierarchy h4 label:hover span { .tsd-hierarchy { .tsd-hierarchy-target { .tsd-hierarchy-toggle { .tsd-full-hierarchy:not(:last-child) { .tsd-full-hierarchy, .tsd-full-hierarchy ul { .tsd-full-hierarchy a { .tsd-full-hierarchy svg[data-dropdown] { .tsd-full-hierarchy svg[data-dropdown="false"] { .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { .tsd-panel-group.tsd-index-group { .tsd-index-panel .tsd-index-list { ⋮---- .tsd-index-panel .tsd-index-list li { .tsd-flag { .tsd-anchor { .tsd-member { .tsd-member .tsd-anchor + h3 { .tsd-navigation.settings { .tsd-navigation > a, .tsd-navigation a, .tsd-navigation a.current, .tsd-navigation a:hover, .tsd-navigation ul, .tsd-navigation li, .tsd-navigation .tsd-nav-link { .tsd-nested-navigation { .tsd-nested-navigation > li > details { .tsd-small-nested-navigation { .tsd-small-nested-navigation > li > details { .tsd-page-navigation-section > summary { .tsd-page-navigation-section > summary > svg { .tsd-page-navigation-section > div { .tsd-page-navigation ul { #tsd-sidebar-links a { #tsd-sidebar-links a:last-of-type { a.tsd-index-link { .tsd-accordion-summary { ⋮---- list-style-type: none; /* hide marker on non-safari */ outline: none; /* broken on safari, so just hide it */ ⋮---- .tsd-accordion-summary::-webkit-details-marker { ⋮---- display: none; /* hide marker on safari */ ⋮---- .tsd-accordion-summary, .tsd-accordion-summary a { .tsd-accordion-summary > * { /* * We need to be careful to target the arrow indicating whether the accordion * is open, but not any other SVGs included in the details element. */ .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child { .tsd-index-content > :not(:first-child) { .tsd-index-summary { .tsd-no-select { .tsd-kind-icon { .tsd-signature > .tsd-kind-icon { .tsd-panel { .tsd-panel.tsd-member { .tsd-panel:empty { .tsd-panel > h1, .tsd-panel > h1.tsd-before-signature, .tsd-panel-group { ⋮---- .tsd-panel-group.tsd-index-group details { .tsd-panel-group > .tsd-accordion-summary { #tsd-search[open] { #tsd-search[open].closing { /* Avoid setting `display` on closed dialog */ ⋮---- /* Anchor dialog to top */ ⋮---- #tsd-search-input { ⋮---- padding: 0 0.625rem; /* 10px */ ⋮---- #tsd-search-input:focus-visible { #tsd-search-input::placeholder { #tsd-search-results { #tsd-search-results:not(:empty) { #tsd-search-results > li { #tsd-search-results > li:nth-child(even) { #tsd-search-results > li:is(:hover, [aria-selected="true"]) { /* It's important that this takes full size of parent `li`, to capture a click on `li` */ #tsd-search-results > li > a { #tsd-search-results > li > a > .text { #tsd-search-results > li > a .parent { #tsd-search-results > li > a mark { #tsd-search-status { #tsd-search-status:not(:empty) { .tsd-signature { .tsd-signature-keyword { .tsd-signature-symbol { .tsd-signature-type { .tsd-signatures { .tsd-signatures .tsd-signature { .tsd-signatures .tsd-index-signature:not(:last-child) { .tsd-signatures .tsd-index-signature .tsd-signature { .tsd-description .tsd-signatures .tsd-signature { ul.tsd-parameter-list, ul.tsd-parameter-list > li.tsd-parameter-signature, ul.tsd-parameter-list h5, .tsd-sources { .tsd-sources a { .tsd-sources ul { .tsd-page-toolbar { .tsd-page-toolbar a { .tsd-toolbar-contents { .tsd-toolbar-contents > .title { #tsd-toolbar-links { .tsd-widget { .tsd-widget:hover { .tsd-widget:active { #tsd-toolbar-menu-trigger { .tsd-member-summary-name { .tsd-anchor-icon { .tsd-anchor-icon svg { .tsd-member-summary-name:hover > .tsd-anchor-icon svg, .deprecated { .warning { .tsd-kind-project { .tsd-kind-module { .tsd-kind-namespace { .tsd-kind-enum { .tsd-kind-enum-member { .tsd-kind-variable { .tsd-kind-function { .tsd-kind-class { .tsd-kind-interface { .tsd-kind-constructor { .tsd-kind-property { .tsd-kind-method { .tsd-kind-reference { .tsd-kind-call-signature { .tsd-kind-index-signature { .tsd-kind-constructor-signature { .tsd-kind-parameter { .tsd-kind-type-parameter { .tsd-kind-accessor { .tsd-kind-get-signature { .tsd-kind-set-signature { .tsd-kind-type-alias { /* if we have a kind icon, don't color the text by kind */ .tsd-kind-icon ~ span { /* mobile */ ⋮---- /* temporary fix to vertically align, for compatibility */ ⋮---- .col-content { .col-sidebar { .col-sidebar > *:last-child { .overlay { .to-has-menu .overlay { .to-has-menu .col-sidebar { .from-has-menu .overlay { .from-has-menu .col-sidebar { .has-menu body { .has-menu .overlay { .has-menu .col-sidebar { .has-menu .tsd-navigation { ⋮---- /* one sidebar */ ⋮---- .site-menu { ⋮---- /* two sidebars */ ⋮---- .page-menu { ⋮---- .page-menu, # Desktop UI/UX Review PC画面(1280x800, 一般的なラップトップ相当)で全ゲーム画面をagent-browserで操作・撮影し、UI/UX課題を抽出してGitHub Issueを作成する。 ## 前提条件 - [agent-browser](https://github.com/vercel-labs/agent-browser) がインストール済み(`npm install -g agent-browser && agent-browser install`) - Go サーバーが起動可能な状態 - `gh` CLIでGitHubにログイン済み - 画像アップロード先として catbox.moe を使用(永続的な無料ホスティング、APIキー不要) ## 手順 ### 1. 環境準備 残留プロセスを停止し、Goサーバーをバックグラウンドで起動する。 ```sh pkill -f 'go run' || true; pkill -f vitest || true; pkill -f 'bun run' || true sleep 1 PORT=8080 go run ./cmd/server & # サーバー起動待ち(最大10秒) for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/ | grep -q 200 && break; sleep 1; done ``` ### 2. ゲーム一覧の取得 ```sh mkdir -p /tmp/desktop-screenshots # ゲーム一覧を gameRoutes.ts から動的に取得(信頼できるソース) ROOT_GAME=$(grep -P "path:\s*'/'" frontend/src/constants/gameRoutes.ts | grep -oP "labelKey:\s*'nav\.\K[^']*") ALL_GAMES=$(grep -oP "path:\s*'/\K[^']*" frontend/src/constants/gameRoutes.ts | sed "s/^$/$ROOT_GAME/" | tr '\n' ' ' | sed 's/ $//') GAMES="${ARGUMENTS:-$ALL_GAMES}" echo "対象ゲーム: $GAMES" ``` ### 3. agent-browserによるスクリーンショット撮影 agent-browser CLIを使い、デスクトップビューポートでゲーム画面を撮影・確認する。 **3a. ブラウザ起動・ビューポート設定** ```sh agent-browser open "http://localhost:8080/" agent-browser set viewport 1280 800 ``` **3b. 各ゲームについて以下を繰り返す** 各ゲームについて、以下の操作をagent-browserで実行する: ```sh for game in $GAMES; do path="/#/$game" [ "$game" = "$ROOT_GAME" ] && path="/" # 1. ページ遷移 agent-browser open "http://localhost:8080$path" # 2. ゲームコンテンツの読み込み待ち agent-browser wait '[aria-live="polite"]' # 3. 初期表示スクショ(チュートリアルダイアログ付き) agent-browser screenshot "/tmp/desktop-screenshots/${game}.png" --full # 4. チュートリアルスキップ(スキップボタンがあればクリック) agent-browser snapshot agent-browser find role button click --name "スキップ" 2>/dev/null || true # 5. プレイ画面スクショ agent-browser wait 300 agent-browser screenshot "/tmp/desktop-screenshots/${game}-play.png" --full done ``` **3c. ナビゲーションメニュー撮影** ```sh agent-browser open "http://localhost:8080/" agent-browser wait '[aria-live="polite"]' agent-browser find role button click --name "スキップ" 2>/dev/null || true agent-browser wait 300 # ハンバーガーメニューが存在する場合はクリック(PC版ではメニューバーかもしれない) agent-browser find role button click --name "メニューを開く" 2>/dev/null || true agent-browser wait 500 agent-browser screenshot "/tmp/desktop-screenshots/nav-open.png" --full ``` **3d. PC固有のインタラクション確認** プレイ画面で以下のPC固有操作を確認する: ```sh # hover状態の視覚フィードバック確認(カードやボタンにマウスオーバー) agent-browser snapshot # snapshot結果から主要なボタン/カードの参照IDを取得し、hover確認 agent-browser mouse move # ボタン座標にマウス移動 agent-browser screenshot "/tmp/desktop-screenshots/hover-check.png" # キーボードアクセシビリティ確認(Tab移動) agent-browser press Tab agent-browser press Tab agent-browser screenshot "/tmp/desktop-screenshots/keyboard-focus.png" ``` ### 4. スクリーンショット確認 Read ツールで各スクリーンショットを読み込み、視覚的に確認する。 #### 確認観点(最重要: スクロールなしで遊べるか) 最優先の観点は**ビューポート内にゲームのすべての操作・情報が収まり、スクロールなしでストレスなく遊べるか**。デスクトップでも同様に、プレイ中にスクロールが必要な状態は課題である。 **PC固有の観点** - **ビューポート収まり**: プレイ中の全要素(カード、ボタン、スコア等)が1280x800のビューポート内に収まるか。縦スクロール・横スクロールともに発生しないことが理想 - **ワイドスクリーン活用**: 横幅を活かしてビューポート内に全要素を無理なく配置できているか。ただし「空白を埋めること」自体を目的にしない - **マウスインタラクション**: hover状態の視覚フィードバック、ドラッグ&ドロップ対応の有無 - **キーボードアクセシビリティ**: Tab移動、Enter/Space操作、ショートカットキーの有無 - **ナビゲーション**: PC版でもハンバーガーメニューのままか。サイドバーやトップメニューバーが適切か **共通の観点** - **チュートリアルダイアログ**: 背景との干渉、画面中央配置の適切さ - **カード表示**: 背景とのコントラスト、視認性。ビューポートに収まる範囲でのサイズ調整 - **テキスト**: フォントサイズの適切さ、行長の読みやすさ。ビューポート内に収めるための情報密度の最適化 - **ボタン/クリック対象**: クリック可能であることの視覚的手がかり。ただしサイズを大きくしてスクロールが発生するなら小さいほうが良い - **レイアウト**: ビューポート内にすべてが収まるか。収まらない場合、何を削るか・折りたたむかの優先順位 - **デザイン一貫性**: 背景色、ボタンスタイル、テーマ統一 #### 設計原則による判断基準 **「そのデバイスでスクロールせずに快適に遊べるか」**が唯一最大の判断軸。症状ではなく、以下の設計原則で課題かどうかを判断する: - **スクロールゼロが最優先**: ゲームプレイ中にスクロールが必要な状態は原則として課題。要素を大きくする、情報を追加する等の「改善」がスクロールを生むなら、それは改悪 - **ビューポートに収めるための妥協は許容**: ボタンやカードが小さくても、ビューポート内に全要素が収まりゲームが成立するならOK。逆に大きく見やすくてもスクロールが必要なら問題 - **参照情報はオンデマンド**: 配当表・ルール説明等の参照情報は折りたたみ表示が原則。展開して空白を埋めるのは本末転倒 - **アクション誘導**: ユーザーが次のアクションを迷わずできるか。ただしCTAの明確さのためにスクロールが発生する設計は不可 - **一貫性 > 個別最適**: 同種のゲーム(例: Video Poker系3ゲーム)は同じレイアウトパターンを共有すべき ### 5. 既存Issue確認(起票前に必須) 課題を発見したら、**起票前に必ず以下を実行**する: ```sh # 類似の既存issueを検索(open + closed両方) gh issue list --search "<課題のキーワード>" --state all --limit 20 ``` 確認事項: - **同一の課題が過去に起票されていないか**(open/closed問わず) - **過去に逆方向の対応がされていないか**(例: 「空白を埋める」→「情報過多を減らす」の振り子パターン) - 矛盾する過去対応がある場合は**新規issueを起票せず**、既存issueにコメントで経緯と根本原因の考察を残す - 同一課題がclosedで再発している場合は、既存issueを再openするか、根本原因を特定した新issueを起票する ### 6. 課題分類 発見した課題を以下の重大度で分類する: | 重大度 | 基準 | |--------|------| | HIGH | ゲームプレイ不能、またはプレイ中にスクロールが必須 | | MEDIUM | スクロールなしで遊べるが操作性や視認性に問題がある | | LOW | 見た目の統一感、細かい改善点(ゲームプレイに支障なし) | ### 7. GitHub Issue作成 各課題ごとにGitHub Issueを作成する。 **ラベル**: `ui/ux` + `bug`(既存の問題)or `enhancement`(改善提案) **Issue本文テンプレート**: ```markdown ## 問題 [具体的な問題の説明] ### 影響箇所 - [ゲーム名1]: [具体的な症状] - [ゲーム名2]: [具体的な症状] ## 改善案 1. [改善案1] 2. [改善案2] ## 重大度 [HIGH/MEDIUM/LOW] — [理由] ## 対象デバイス Desktop (1280x800) ``` ### 8. スクリーンショット添付 catbox.moe に画像をアップロードし、各IssueにコメントでMarkdown画像リンクを添付する。 ```sh # アップロード URL=$(curl -s -F "reqtype=fileupload" -F "fileToUpload=@/tmp/desktop-screenshots/game.png" https://catbox.moe/user/api.php) # Issueコメントに添付 gh issue comment --body "## スクリーンショット(Desktop 1280x800) ### ゲーム名 ![ゲーム名]($URL)" ``` ### 9. クリーンアップ ```sh agent-browser close pkill -f 'go run' || true rm -rf /tmp/desktop-screenshots ``` ## agent-browser コマンドリファレンス 本コマンドで使用する主要なagent-browserコマンド: | コマンド | 用途 | |----------|------| | `agent-browser open ` | ページ遷移 | | `agent-browser set viewport 1280 800` | ビューポートをデスクトップサイズに設定 | | `agent-browser wait ` | セレクタ出現の待機 | | `agent-browser wait ` | ミリ秒待機 | | `agent-browser screenshot --full` | フルページスクリーンショット撮影 | | `agent-browser snapshot` | アクセシビリティツリー取得(AI向け要素探索) | | `agent-browser find role button click --name "..."` | ボタンをアクセシビリティ名で検索・クリック | | `agent-browser find text "..." click` | テキストで要素を検索・クリック | | `agent-browser click ` | snapshot参照ID(`@e2`等)で要素クリック | | `agent-browser mouse move ` | マウスを指定座標に移動(hover確認) | | `agent-browser press ` | キー入力(Tab, Enter等のキーボード操作確認) | | `agent-browser is visible ` | 要素の可視状態を確認 | | `agent-browser get text ` | 要素のテキスト取得 | | `agent-browser close` | ブラウザ終了 | **操作のコツ**: - `snapshot` でアクセシビリティツリーを取得し、参照ID(`@e1`, `@e2`...)で要素を特定してから `click @e2` で操作する - `find role button click --name "..."` でアクセシビリティ名ベースの要素探索・操作が可能 - 要素が見つからない場合は `2>/dev/null || true` で握りつぶしてスクリプトを継続する - PC版では `mouse move` でhover状態の確認、`press Tab` でキーボードフォーカスの確認も行う ## 注意事項 - **リソース制約**: WSL2環境(~2GB RAM)のため、サーバー起動中に他の重いタスク(go test, bun run test等)は実行しない - **HashRouter**: URLは `http://localhost:8080/#/` 形式。`/` 直打ちは404になる - **チュートリアルダイアログ**: 初回表示時にほぼ全ゲームで表示される。スキップ状態はlocalStorageに保存される - **catbox.moe**: 永続的な無料ホスティング。APIキー不要。1ファイル200MBまで - ゲームルート一覧は `frontend/src/constants/gameRoutes.ts` で管理されており、本コマンドは同ファイルから動的に取得するため、ゲームの追加・削除時にこのコマンドファイルの更新は不要 - モバイル版のレビュー結果と照合し、PC・モバイル共通の課題は既存Issueにデスクトップのスクリーンショットを追記する(重複Issue作成を避ける) ## 引数 $ARGUMENTS — オプション。特定のゲーム名を指定すると、そのゲームのみレビューする(例: `blackjack holdem`)。省略時は全ゲーム。 # Mobile UI/UX Review スマホ画面(375x667, iPhone SE相当)で全ゲーム画面をagent-browserで操作・撮影し、UI/UX課題を抽出してGitHub Issueを作成する。 ## 前提条件 - [agent-browser](https://github.com/vercel-labs/agent-browser) がインストール済み(`npm install -g agent-browser && agent-browser install`) - Go サーバーが起動可能な状態 - `gh` CLIでGitHubにログイン済み - 画像アップロード先として catbox.moe を使用(永続的な無料ホスティング、APIキー不要) ## 手順 ### 1. 環境準備 残留プロセスを停止し、Goサーバーをバックグラウンドで起動する。 ```sh pkill -f 'go run' || true; pkill -f vitest || true; pkill -f 'bun run' || true sleep 1 PORT=8080 go run ./cmd/server & # サーバー起動待ち(最大10秒) for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/ | grep -q 200 && break; sleep 1; done ``` ### 2. ゲーム一覧の取得 ```sh mkdir -p /tmp/mobile-screenshots # ゲーム一覧を gameRoutes.ts から動的に取得(信頼できるソース) ROOT_GAME=$(grep -P "path:\s*'/'" frontend/src/constants/gameRoutes.ts | grep -oP "labelKey:\s*'nav\.\K[^']*") ALL_GAMES=$(grep -oP "path:\s*'/\K[^']*" frontend/src/constants/gameRoutes.ts | sed "s/^$/$ROOT_GAME/" | tr '\n' ' ' | sed 's/ $//') GAMES="${ARGUMENTS:-$ALL_GAMES}" echo "対象ゲーム: $GAMES" ``` ### 3. agent-browserによるスクリーンショット撮影 agent-browser CLIを使い、モバイルビューポートでゲーム画面を撮影・確認する。 **3a. ブラウザ起動・ビューポート設定** ```sh agent-browser open "http://localhost:8080/" agent-browser set viewport 375 667 ``` **3b. 各ゲームについて以下を繰り返す** 各ゲームについて、以下の操作をagent-browserで実行する: ```sh for game in $GAMES; do path="/#/$game" [ "$game" = "$ROOT_GAME" ] && path="/" # 1. ページ遷移 agent-browser open "http://localhost:8080$path" # 2. ゲームコンテンツの読み込み待ち agent-browser wait '[aria-live="polite"]' # 3. 初期表示スクショ(チュートリアルダイアログ付き) agent-browser screenshot "/tmp/mobile-screenshots/${game}.png" --full # 4. チュートリアルスキップ(スキップボタンがあればクリック) agent-browser snapshot agent-browser find role button click --name "スキップ" 2>/dev/null || true # 5. プレイ画面スクショ agent-browser wait 300 agent-browser screenshot "/tmp/mobile-screenshots/${game}-play.png" --full done ``` **3c. ナビゲーションメニュー撮影** ```sh agent-browser open "http://localhost:8080/" agent-browser wait '[aria-live="polite"]' agent-browser find role button click --name "スキップ" 2>/dev/null || true agent-browser wait 300 agent-browser find role button click --name "メニューを開く" 2>/dev/null || true agent-browser wait 500 agent-browser screenshot "/tmp/mobile-screenshots/nav-open.png" --full ``` ### 4. スクリーンショット確認 Read ツールで各スクリーンショットを読み込み、視覚的に確認する。 #### 確認観点(最重要: スクロールなしで遊べるか) 最優先の観点は**ビューポート内にゲームのすべての操作・情報が収まり、スクロールなしでストレスなく遊べるか**。タップターゲットのサイズを大きくした結果スクロールが発生するなら、それは改善ではなく改悪である。 - **ビューポート収まり**: プレイ中の全要素(カード、ボタン、スコア等)が375x667のビューポート内に収まるか。縦スクロール・横スクロールともに発生しないことが理想 - **チュートリアルダイアログ**: 背景との干渉、視認性 - **カード表示**: 背景とのコントラスト、視認性。ビューポートに収まる範囲でのサイズ調整 - **テキスト**: 溢れ、切れ。ビューポート内に収めるための情報密度の最適化 - **ボタン/タップ対象**: タップ可能であること。ただしサイズを大きくしてスクロールが発生するなら小さいほうが良い - **レイアウト**: ビューポート内にすべてが収まるか。収まらない場合、何を削るか・折りたたむかの優先順位 - **デザイン一貫性**: 背景色、ボタンスタイル、テーマ統一 - **ナビゲーション**: ゲーム数に対する探しやすさ #### 設計原則による判断基準 **「そのデバイスでスクロールせずに快適に遊べるか」**が唯一最大の判断軸。症状ではなく、以下の設計原則で課題かどうかを判断する: - **スクロールゼロが最優先**: ゲームプレイ中にスクロールが必要な状態は原則として課題。タップターゲットを大きくする、情報を追加する等の「改善」がスクロールを生むなら、それは改悪 - **ビューポートに収めるための妥協は許容**: カードやボタンが小さくても、ビューポート内に全要素が収まりゲームが成立するならOK。逆に大きく見やすくてもスクロールが必要なら問題 - **参照情報はオンデマンド**: 配当表・ルール説明等の参照情報は折りたたみ表示が原則。展開して空白を埋めるのは本末転倒 - **アクション誘導**: ユーザーが次のアクションを迷わずできるか。ただしCTAの明確さのためにスクロールが発生する設計は不可 - **一貫性 > 個別最適**: 同種のゲーム(例: Video Poker系3ゲーム)は同じレイアウトパターンを共有すべき ### 5. 既存Issue確認(起票前に必須) 課題を発見したら、**起票前に必ず以下を実行**する: ```sh # 類似の既存issueを検索(open + closed両方) gh issue list --search "<課題のキーワード>" --state all --limit 20 ``` 確認事項: - **同一の課題が過去に起票されていないか**(open/closed問わず) - **過去に逆方向の対応がされていないか**(例: 「空白を埋める」→「情報過多を減らす」の振り子パターン) - 矛盾する過去対応がある場合は**新規issueを起票せず**、既存issueにコメントで経緯と根本原因の考察を残す - 同一課題がclosedで再発している場合は、既存issueを再openするか、根本原因を特定した新issueを起票する ### 6. 課題分類 発見した課題を以下の重大度で分類する: | 重大度 | 基準 | |--------|------| | HIGH | ゲームプレイ不能、またはプレイ中にスクロールが必須 | | MEDIUM | スクロールなしで遊べるが操作性や視認性に問題がある | | LOW | 見た目の統一感、細かい改善点(ゲームプレイに支障なし) | ### 7. GitHub Issue作成 各課題ごとにGitHub Issueを作成する。 **ラベル**: `ui/ux` + `bug`(既存の問題)or `enhancement`(改善提案) **Issue本文テンプレート**: ```markdown ## 問題 [具体的な問題の説明] ### 影響箇所 - [ゲーム名1]: [具体的な症状] - [ゲーム名2]: [具体的な症状] ## 改善案 1. [改善案1] 2. [改善案2] ## 重大度 [HIGH/MEDIUM/LOW] — [理由] ## 対象デバイス Mobile (375x667) ``` ### 8. スクリーンショット添付 catbox.moe に画像をアップロードし、各IssueにコメントでMarkdown画像リンクを添付する。 ```sh # アップロード URL=$(curl -s -F "reqtype=fileupload" -F "fileToUpload=@/tmp/mobile-screenshots/game.png" https://catbox.moe/user/api.php) # Issueコメントに添付 gh issue comment --body "## スクリーンショット(iPhone SE 375x667) ### ゲーム名 ![ゲーム名]($URL)" ``` ### 9. クリーンアップ ```sh agent-browser close pkill -f 'go run' || true rm -rf /tmp/mobile-screenshots ``` ## agent-browser コマンドリファレンス 本コマンドで使用する主要なagent-browserコマンド: | コマンド | 用途 | |----------|------| | `agent-browser open ` | ページ遷移 | | `agent-browser set viewport 375 667` | ビューポートをモバイルサイズに設定 | | `agent-browser wait ` | セレクタ出現の待機 | | `agent-browser wait ` | ミリ秒待機 | | `agent-browser screenshot --full` | フルページスクリーンショット撮影 | | `agent-browser snapshot` | アクセシビリティツリー取得(AI向け要素探索) | | `agent-browser find role button click --name "..."` | ボタンをアクセシビリティ名で検索・クリック | | `agent-browser find text "..." click` | テキストで要素を検索・クリック | | `agent-browser click ` | snapshot参照ID(`@e2`等)で要素クリック | | `agent-browser close` | ブラウザ終了 | **操作のコツ**: - `snapshot` でアクセシビリティツリーを取得し、参照ID(`@e1`, `@e2`...)で要素を特定してから `click @e2` で操作する - `find role button click --name "..."` でアクセシビリティ名ベースの要素探索・操作が可能 - 要素が見つからない場合は `2>/dev/null || true` で握りつぶしてスクリプトを継続する ## 注意事項 - **リソース制約**: WSL2環境(~2GB RAM)のため、サーバー起動中に他の重いタスク(go test, bun run test等)は実行しない - **HashRouter**: URLは `http://localhost:8080/#/` 形式。`/` 直打ちは404になる - **チュートリアルダイアログ**: 初回表示時にほぼ全ゲームで表示される。スキップ状態はlocalStorageに保存される - **catbox.moe**: 永続的な無料ホスティング。APIキー不要。1ファイル200MBまで - ゲームルート一覧は `frontend/src/constants/gameRoutes.ts` で管理されており、本コマンドは同ファイルから動的に取得するため、ゲームの追加・削除時にこのコマンドファイルの更新は不要 ## 引数 $ARGUMENTS — オプション。特定のゲーム名を指定すると、そのゲームのみレビューする(例: `blackjack holdem`)。省略時は全ゲーム。 --- globs: ["frontend/**/*.ts", "frontend/**/*.tsx"] --- # Frontend (TypeScript/TSX) File Editing Rules ## Package Manager **Always use `bun` instead of `npm`, and `bunx` instead of `npx`.** This project uses Bun as the sole JavaScript package manager and script runner. ## Pre-commit Checks (mandatory, all must pass) ```sh cd frontend && bun run build # React build cd frontend && bun run check # Biome lint + format check cd frontend && bun run test # Vitest unit tests ``` ## Testing **Unit tests are mandatory.** Include them in the same commit as the implementation. Test stack: **Vitest + React Testing Library + jest-dom** ### TDD Cycle (Red → Green → Refactor) Always follow this cycle before implementing: 1. **Red** — Write a failing test first. Create a test that captures the expected behavior before writing implementation code: ```sh cd frontend && bun run test -- --run TestNewFeature # Fails (Red) ``` 2. **Green** — Write the minimum code to pass the test. Do not add extra functionality: ```sh cd frontend && bun run test -- --run TestNewFeature # Passes (Green) ``` 3. **Refactor** — Clean up code while keeping tests green. Improve naming, structure, and remove duplication: ```sh cd frontend && bun run test # All tests pass (after Refactor) ``` ### Coverage Standard **Branch coverage (C1) of 80% or higher** is required for the following 4 directories: - `frontend/src/api` - `frontend/src/components` - `frontend/src/pages` - `frontend/src/utils` Focus on business logic and critical paths. Forced coverage of unreachable branches is unnecessary. ### Test Locations (by layer) | Layer | Test file | What to test | |-------|-----------|-------------| | API client | `src/api/*.test.ts` | URL, request body, error handling | | Components | `src/components/*.test.tsx` | Rendering, props, event handlers | | Pages | `src/pages/*.test.tsx` | On-mount API calls, phase-specific rendering, button interactions | ### Test Patterns - **API mocks**: Mock the API module with `vi.mock('../api/gameApi', ...)`; access via `vi.mocked(api.exec)` - **Router-dependent components**: Wrap components using `useLocation` (e.g., `NavBar`) in `` - **Async effect waiting**: Use `waitFor(() => expect(...))` for components that call APIs in `useEffect` - **Button queries**: When text appears in multiple elements, use `screen.getByRole('button', { name: '...' })` - **QueryClientProvider wrapping**: Page tests and hook tests must use `renderWithProviders` (`frontend/src/test/renderWithProviders.tsx`) ## i18n (Internationalization) The Web GUI supports Japanese (ja) / English (en) via `react-i18next` + `i18next-browser-languagedetector`. - **Config**: `frontend/src/i18n/index.ts` - **Translation files**: `frontend/src/i18n/locales/{ja,en}/.json` - **In components**: use the `useTranslation()` hook - **In non-component files** (e.g., `playerUtils.ts`): import the `i18n` instance directly - **Tests**: ja translations are initialized in `frontend/src/test/setup.ts` ## Dead Code - Always remove dead code encountered when modifying code - Detection tool: `knip` - Verify manually before deleting (beware of false positives from static analysis) --- globs: ["**/*.go"] --- # Go File Editing Rules ## Format - **Always run `goimports -w ` before committing** (do not use `gofmt`) ## Lint - **Run `golangci-lint run ./...` before committing and ensure no warnings or errors** ## Testing **Unit tests are mandatory.** Include them in the same commit as the implementation. ### TDD Cycle (Red → Green → Refactor) Always follow this cycle before implementing: 1. **Red** — Write a failing test first. Create a test that captures the expected behavior before writing implementation code: ```sh go test -tags test ./path/to/package -run TestNewFeature # Fails (Red) ``` 2. **Green** — Write the minimum code to pass the test. Do not add extra functionality: ```sh go test -tags test ./path/to/package -run TestNewFeature # Passes (Green) ``` 3. **Refactor** — Clean up code while keeping tests green. Improve naming, structure, and remove duplication: ```sh go test -tags test ./... # All tests pass (after Refactor) ``` Run tests: `go test -tags test ./...` ### Coverage Standard - `cmd/` and `internal/infrastructure/` are excluded from coverage - All other packages under `internal/` require **branch coverage (C1) of 80% or higher** - Focus on business logic and critical paths ### Test Locations (by layer) | Layer | Test file | |-------|-----------| | Domain | `internal/domain/*_test.go` | | Use cases | `internal/usecase/*Interactor_test.go` | | Presenters | `internal/adapter/presenter/*_test.go` | | Controllers | `internal/adapter/controller/*_test.go` | ### Mock Pattern - **Presenter mocks**: `internal/usecase/presenter/*_mock.go` — implemented with `testify/mock` - **Interactor mocks**: `internal/adapter/controller/usecase/*_mock.go` — implemented with `testify/mock` - Use existing `BlackJack*_mock.go` as reference pattern ### Writing Deterministic Tests Do not depend on shuffle order: - Set up hands manually with `AddCard`; do not depend on order after `Reset`/`Shuffle` - Give dealer/CPU scores that prevent automatic draws (e.g., BlackJack dealer >= 17) - Use retry loops up to 1000 iterations to cover both branches of random decisions ## Architecture Clean Architecture: `infrastructure` → `adapter` → `usecase` → `domain` - Domain interfaces: `internal/domain/interfaces/` - Never reverse the dependency direction (outer layers depend on inner layers) ## Dead Code - Always remove dead code encountered when modifying code - Detection tool: `golang.org/x/tools/cmd/deadcode` - Verify manually before deleting (beware of false positives such as reflection-based calls) --- name: doc-drift-check description: Check all project documentation for discrepancies with the actual codebase and report findings disable-model-invocation: true allowed-tools: Read, Grep, Glob, Bash, Agent argument-hint: "[--fix]" --- # Documentation Drift Check Verify that all project documentation matches the actual codebase. Report discrepancies and optionally fix them. ## Scope Check the following documents against the actual code: ### 1. Game list consistency - `CLAUDE.md` Commands section — game CLI commands - `README.md` — game descriptions and run commands - `docs/games.md` — game entity definitions - `docs/manual/cui/` and `docs/manual/web/` — per-game manuals - Actual games: registered in `cmd/trumpcards/main.go` and domain files in `internal/domain/` ### 2. Web API endpoints - `docs/architecture.md` — endpoint list and count - `api/openapi.yaml` — endpoint definitions - Actual endpoints: registered in `internal/infrastructure/web/TrumpCardsWeb.go` ### 3. Frontend - `frontend/CLAUDE.md` — i18n translation file list - Actual translation files: `frontend/src/i18n/locales/{ja,en}/*.json` - Actual pages: `frontend/src/pages/*Page.tsx` ### 4. UML design documents - `docs/design/backend.md` — class, sequence, state machine diagrams for Go backend - Domain structs/interfaces vs actual code in `internal/domain/` and `internal/domain/interfaces/` - Game list in diagrams vs actual games - Phase constants in state machine diagrams vs actual phase definitions - `docs/design/frontend.md` — class, sequence, state machine diagrams for React frontend - Component/hook/API type names vs actual files in `frontend/src/` - Phase enums vs actual definitions in `frontend/src/types/phases.ts` - Game route categories vs `frontend/src/constants/gameRoutes.ts` ### 5. ADRs - `docs/adr/README.md` index table — must list all ADR files in `docs/adr/` - ADR status consistency ### 6. Version info - `CLAUDE.md` Requirements table (Go, Node.js, Bun versions) - `go.mod` Go version - Actual installed versions ### 7. Auto-memory (MEMORY.md) - Game list and endpoint count in MEMORY.md vs actual ## Procedure 1. Use parallel Explore agents to check each scope area simultaneously 2. Collect all discrepancies found 3. Report results in a structured format: - **Discrepancies found**: file, location, current value, expected value - **No discrepancies**: confirmed items 4. If `$ARGUMENTS` contains `--fix`: - Fix all discrepancies directly in the files - Create a GitHub issue documenting what was fixed - Do NOT commit (leave changes unstaged for user review) 5. If `$ARGUMENTS` does NOT contain `--fix`: - Create a GitHub issue listing all discrepancies found - Do not modify any files --- name: make-issue description: ソースコードをDRY/KISS/YAGNI観点で解析し、改善提案をGitHub Issueとして登録する disable-model-invocation: true allowed-tools: Read, Grep, Glob, Bash, Agent --- # 役割 あなたは経験豊富なシニアソフトウェアエンジニアであり、優秀なコードレビュアーです。コードの品質向上、保守性の担保、およびパフォーマンス最適化に長けています。 # 目的 現在のカレントディレクトリにあるローカルリポジトリのソースコードを解析し、潜在的な課題の抽出と具体的な改善提案を行い、それをGitHub Issueとして登録してください。 # 実行ステップ 以下のステップに従って自律的に作業を進めてください。 ## Step 1: ソースコードの解析と課題抽出 カレントディレクトリ内のファイル構造と主要なソースコード(設定ファイル、メインのロジック、テストなど)を読み込んでください。以下の観点でコードを評価し、最も優先度が高いと思われる課題を抽出してください。 特に、以下の**ソフトウェア設計原則**に反している箇所を重点的に探してください: - **DRY (Don't Repeat Yourself)**: コードやロジックの重複がないか。モジュール化や共通化で解決できる部分はないか。 - **KISS (Keep It Simple, Stupid)**: 過剰に複雑なロジックや、無駄に難解な実装(オーバーエンジニアリング)になっていないか。よりシンプルに書けないか。 - **YAGNI (You Aren't Gonna Need It)**: 「将来使うかもしれない」という理由で作られた現状不要な機能、過剰な汎用化、または全く使われていないデッドコードがないか。 その他の一般的な観点: - バグの可能性、エッジケースの考慮漏れ - 命名規則の不備など、一般的な可読性の低さ - パフォーマンスのボトルネックやセキュリティ上の懸念 ## Step 2: 改善提案(Issue本文)の作成 抽出した課題について、以下の構成でIssueの本文(Markdown形式)を考案してください。 1. **課題の概要 (Problem)**: どのファイルの、何が問題なのか。(DRY, KISS, YAGNIのどの原則に反しているかも明記すること) 2. **原因/背景 (Cause/Context)**: なぜそれが問題なのか、どのような悪影響(保守性の低下など)があるのか。 3. **改善提案 (Proposed Solution)**: 具体的にどのようにコードを修正すべきか。(※可能な限り、Before/Afterのサンプルコードを記述してください) 4. **期待される効果 (Impact)**: 修正によって得られるメリット(コード行数の削減、可読性の向上など)。 ## Step 3: GitHub Issueの作成 (`gh` コマンドの実行) Step 2で作成した内容をもとに、`gh` コマンドを使用して対象リポジトリにIssueを作成してください。 ※ コマンド実行時の改行やエスケープ処理のエラーを防ぐため、必ず以下の手順で実行してください。 1. コマンドラインツールを使用し、Issueの本文を `_temp_issue_body.md` という一時ファイルに書き出してください。 2. 以下の `gh` コマンドを実行してIssueを作成してください。 `gh issue create --title "[Refactor] <簡潔な課題のタイトル>" --body-file _temp_issue_body.md` ※必要に応じて `--label "refactor"` などのオプションを付与しても構いません。 3. Issueの作成が成功したことを確認した後、一時ファイル `_temp_issue_body.md` を削除してください。 # 制約事項・ルール - あなたが行うのは「コードの読み込み」と「Issueの作成」のみです。ソースコード自体の直接的な編集・コミットは行わないでください。 - `gh` コマンドが利用可能である(ログイン済みである)前提で進めてください。 - 複数の重大な課題が見つかった場合は、独断で全てを作成するのではなく、まず概要をリストアップして「どれをIssue化しますか?」とユーザーに確認を求めてください。 さあ、カレントディレクトリの解析から開始してください。 --- name: make-issue-cli description: CLIのUX(ヘルプ、エラー処理、終了コード等)を解析し、改善提案をGitHub Issueとして登録する disable-model-invocation: true allowed-tools: Read, Grep, Glob, Bash, Agent --- # 役割 あなたは経験豊富なシニアソフトウェアエンジニアであり、CLI(コマンドラインインターフェース)ツールのUX設計のエキスパートです。POSIX標準やGNUコーディング規約に精通しており、人間にとってもシェルスクリプトにとっても使いやすい、洗練されたCLIツールの設計に長けています。 # 目的 現在のカレントディレクトリにあるローカルリポジトリ(CLIツールのソースコード)を解析し、CLI特有のUI/UX観点での潜在的な課題を抽出し、具体的な改善提案を含むGitHub Issueとして登録してください。 # 実行ステップ 以下のステップに従って自律的に作業を進めてください。 ## Step 1: ソースコードの解析とCLI UX課題の抽出 カレントディレクトリ内のソースコード(特に引数解析のロジック、標準出力/標準エラー出力のハンドリング、終了コードの設定など)を読み込んでください。以下の観点でコードを評価し、最も優先度が高いと思われる課題を抽出してください。 - **直感性とドキュメンテーション (Discoverability)**: - ヘルプメッセージ(`--help`, `-h`)は分かりやすく、具体的な使用例(Examples)が含まれているか。 - コマンドやオプションの命名は直感的で、標準的なコンベンション(例: `--version`, `--verbose`)に沿っているか。 - **エラーハンドリングと親切さ (Actionable Errors)**: - エラーメッセージは専門用語やスタックトレースをそのまま出すのではなく、「何が起きたか」「どうすれば解決するか」をユーザーに提示しているか。 - エラーメッセージが標準出力(stdout)ではなく、正しく標準エラー出力(stderr)に出力されているか。 - **フィードバックと進行状況 (Feedback & Progress)**: - 実行に時間がかかる処理において、ユーザーを不安にさせないためのプログレスバーやスピナーが実装されているか。 - 成功時のメッセージや、現在の処理状態が適切にフィードバックされているか。 - **安全性と対話性 (Safety & Interactivity)**: - ファイルの削除や上書きなど、破壊的な操作の前に確認プロンプト(例: `Are you sure? [y/N]`)や `--force` オプションの要求が実装されているか。 - 必須引数が欠けている場合に即座にクラッシュするのではなく、対話的に入力を求めるなどのフォールバックがあるか。 - **環境への配慮とスクリプトビリティ (Environment & Scriptability)**: - 成功時は `0`、エラー時は `0以外` の適切な終了コード(Exit Code)を返しているか。 - 出力に色を付ける場合、パイプライン処理時や `NO_COLOR` 環境変数、`--no-color` オプションによる無効化を考慮しているか。 ### 振り子現象(Pendulum Effect)に注意 CLI UXのベストプラクティス(例: 「エラーはstderrへ」「プロンプトはstderrへ」)は一般論として正しいが、**対象プロダクトの実際のユースケースを無視して機械的に適用すると、かえってUXを悪化させる「振り子現象」を引き起こす**。 課題を報告する前に、以下の自問を行うこと: 1. **この出力をパイプやリダイレクトで分離する現実的なユースケースがあるか?** - インタラクティブゲームの対話出力をパイプする人はほぼいない → stdout/stderr の分離は過剰 - `update` コマンドの進捗表示はスクリプトから呼ばれうる → stderr が適切 2. **修正によって、主要なユースケース(ターミナルで直接使う)の体験が悪化しないか?** - stderrへの移動でターミナルでの表示順序がずれる、バッファリングが変わる等のリスク 3. **そのベストプラクティスは、このツールのカテゴリ(ゲーム/ユーティリティ/デーモン等)に適合するか?** - パイプライン用CLI(jq, grep等)と対話型ゲームCLIでは求められるスクリプタビリティが異なる **判定基準:** 「POSIX的に正しいか」ではなく「この変更でユーザーの体験が実際に良くなるか」で判断する。疑わしい場合は Issue 候補として報告し、ユーザーに判断を委ねる。 ## Step 2: 改善提案(Issue本文)の作成 抽出した課題について、以下の構成でIssueの本文(Markdown形式)を考案してください。 1. **課題の概要 (Problem)**: どのファイルの、どのような実装がCLIとしての使い勝手を損ねているか。 2. **ユーザーへの影響 (User/Developer Impact)**: この実装により、ツールを直接使うユーザー、またはシェルスクリプトに組み込んで使う開発者にどのような不便が生じるか。 3. **改善提案 (Proposed Solution)**: 具体的にどのようにコード(引数解析、エラー出力、終了コードなど)を修正すべきか。(※可能な限り、Before/Afterのコードスニペットを記述してください) 4. **期待される効果 (Expected Impact)**: 修正によってCLIの使い勝手や堅牢性がどう向上するか。 ## Step 3: GitHub Issueの作成 (`gh` コマンドの実行) Step 2で作成した内容をもとに、`gh` コマンドを使用して対象リポジトリにIssueを作成してください。 ※ コマンド実行時の改行やエスケープ処理のエラーを防ぐため、必ず以下の手順で実行してください。 1. コマンドラインツールを使用し、Issueの本文を `_temp_issue_body.md` という一時ファイルに書き出してください。 2. 以下の `gh` コマンドを実行してIssueを作成してください。 `gh issue create --title "[CLI UX] <簡潔な課題のタイトル>" --body-file _temp_issue_body.md` ※必要に応じて `--label "cli-ux,enhancement"` などのオプションを付与しても構いません。 3. Issueの作成が成功したことを確認した後、一時ファイル `_temp_issue_body.md` を削除してください。 # 制約事項・ルール - あなたが行うのは「コードの読み込み」と「Issueの作成」のみです。ソースコード自体の直接的な編集・コミットは行わないでください。 - `gh` コマンドが利用可能である(ログイン済みである)前提で進めてください。 - 複数の重大な課題が見つかった場合は、独断で全てを作成するのではなく、まず概要をリストアップして「どれをIssue化しますか?」とユーザーに確認を求めてください。 さあ、カレントディレクトリの解析から開始してください。 --- name: make-issue-web description: フロントエンドのUI/UX・アクセシビリティを解析し、改善提案をGitHub Issueとして登録する disable-model-invocation: true allowed-tools: Read, Grep, Glob, Bash, Agent --- # 役割 あなたは経験豊富なシニアUI/UXデザイナー兼フロントエンドエンジニアです。ユーザー中心設計(UCD)の原則を深く理解しており、ソースコードの実装からユーザー体験のボトルネックを見抜き、改善を提案することに長けています。 # 目的 現在のカレントディレクトリにあるローカルリポジトリ(主にフロントエンドのコード)を解析し、UI/UXおよびアクセシビリティ観点での潜在的な課題を抽出し、具体的な改善提案を含むGitHub Issueとして登録してください。 # 実行ステップ 以下のステップに従って自律的に作業を進めてください。 ## Step 1: ソースコードの解析とUI/UX課題の抽出 カレントディレクトリ内のコンポーネント(React, Vue, HTMLなど)、スタイルシート(CSS, Tailwindなど)、およびフロントエンドのロジックを読み込んでください。以下の観点でコードを評価し、最も優先度が高いと思われる課題を抽出してください。 - **一貫性 (Consistency)**: デザインシステムやテーマ(カラースキーム、タイポグラフィ、余白)から逸脱したハードコードされたスタイルがないか。似たような機能で異なるUIコンポーネントが使われていないか。 - **アクセシビリティ (Accessibility / a11y)**: セマンティックなHTMLが使われているか(例: `div`の代わりに`button`)。`alt`属性の抜け、ARIA属性の不足、コントラスト比が低そうな色の指定、キーボードナビゲーションの考慮漏れがないか。 - **フィードバックと状態管理 (Feedback & State)**: ローディング状態(スピナーやスケルトン)、エラー時のメッセージ表示、成功時のフィードバックなど、ユーザーのアクションに対する適切なシステム応答が実装されているか。 - **認知的負荷とユーザビリティ (Cognitive Load & Usability)**: フォームの入力項目が多すぎる、バリデーションのタイミングが不適切、ユーザーが迷いやすい複雑なDOM構造や導線になっていないか。 - **レスポンシブ対応 (Responsiveness)**: 固定値(px)の多用によるスマートフォンやタブレットでの表示崩れのリスクがないか。 ### 振り子現象(Pendulum Effect)に注意 UI/UXのベストプラクティス(例: 「セマンティックHTMLを使え」「ローディング状態を表示せよ」「a11yを担保せよ」)は一般論として正しいが、**対象プロダクトの実際のユースケースやユーザー層を無視して機械的に適用すると、かえってUXを悪化させたり、不要な複雑性を持ち込む「振り子現象」を引き起こす**。 課題を報告する前に、以下の自問を行うこと: 1. **この変更で恩恵を受けるユーザーは現実的に存在するか?** - カードゲームのアニメーション中にスケルトンローディングを挟むと、逆にテンポが悪くなる - 対話型ゲームの1手ごとにトースト通知を出すと煩わしい 2. **修正によって、主要なユースケース(ゲームをプレイする)の体験が悪化しないか?** - a11y のために aria-live で毎ターン読み上げると、スクリーンリーダーユーザーにとっても情報過多になりうる - セマンティックHTML化が既存のスタイリングやインタラクションを壊すリスク 3. **そのベストプラクティスは、このプロダクトのカテゴリ(ゲーム/業務アプリ/ECサイト等)に適合するか?** - 業務アプリの厳密なフォームバリデーションとカードゲームの入力UXでは求められる水準が異なる - ゲームUIでは「楽しさ」「テンポ」が最優先であり、情報アーキテクチャの教科書的な正しさは二の次 **判定基準:** 「WCAG/デザイン原則的に正しいか」ではなく「この変更でユーザーの体験が実際に良くなるか」で判断する。疑わしい場合は Issue 候補として報告し、ユーザーに判断を委ねる。 ## Step 2: 改善提案(Issue本文)の作成 抽出した課題について、以下の構成でIssueの本文(Markdown形式)を考案してください。 1. **課題の概要 (Problem)**: どのファイルの、どのような実装がUI/UX上の問題を引き起こしているか。(一貫性、アクセシビリティなどのどの原則に反しているか明記) 2. **ユーザーへの影響 (User Impact)**: この実装により、ユーザー(またはスクリーンリーダーなどの支援技術の利用者)にどのような不便や悪影響が生じるか。 3. **改善提案 (Proposed Solution)**: 具体的にどのようにコード(マークアップ、スタイル、ロジック)を修正すべきか。(※可能な限り、Before/Afterのコードスニペットを記述してください) 4. **期待される効果 (Expected Impact)**: 修正によってユーザー体験がどう向上するか、または保守性がどう改善されるか。 ## Step 3: GitHub Issueの作成 (`gh` コマンドの実行) Step 2で作成した内容をもとに、`gh` コマンドを使用して対象リポジトリにIssueを作成してください。 ※ コマンド実行時の改行やエスケープ処理のエラーを防ぐため、必ず以下の手順で実行してください。 1. コマンドラインツールを使用し、Issueの本文を `_temp_issue_body.md` という一時ファイルに書き出してください。 2. 以下の `gh` コマンドを実行してIssueを作成してください。 `gh issue create --title "[UI/UX] <簡潔な課題のタイトル>" --body-file _temp_issue_body.md` ※必要に応じて `--label "ui/ux,enhancement"` などのオプションを付与しても構いません。 3. Issueの作成が成功したことを確認した後、一時ファイル `_temp_issue_body.md` を削除してください。 # 制約事項・ルール - あなたが行うのは「コードの読み込み」と「Issueの作成」のみです。ソースコード自体の直接的な編集・コミットは行わないでください。 - 画像や実際の画面を視覚的に確認することはできないため、あくまで「コードから読み取れる構造・スタイル・ロジック」を根拠にUI/UXの課題を指摘してください。 - `gh` コマンドが利用可能である(ログイン済みである)前提で進めてください。 - 複数の重大な課題が見つかった場合は、独断で全てを作成するのではなく、まず概要をリストアップして「どれをIssue化しますか?」とユーザーに確認を求めてください。 さあ、カレントディレクトリの解析から開始してください。 { "env": { "GOFLAGS": "-tags=test" }, "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "jq -r '.tool_input.command' | { read -r cmd; case \"$cmd\" in git\\ commit*) if git diff --cached --name-only | grep -q '\\.go$'; then output=$(golangci-lint run ./... 2>&1 | head -30); if [ -n \"$output\" ]; then printf '{\"continue\": false, \"stopReason\": \"golangci-lint failed:\\n%s\"}' \"$output\"; else echo '{}'; fi; else echo '{}'; fi ;; *) echo '{}' ;; esac; }", "timeout": 120, "statusMessage": "Running golangci-lint..." }, { "type": "command", "command": "jq -r '.tool_input.command' | { read -r cmd; case \"$cmd\" in git\\ commit*) if git diff --cached --name-only | grep -qE '\\.(ts|tsx)$'; then cd frontend && output=$(bun run check 2>&1); if echo \"$output\" | grep -q 'Found'; then printf '{\"continue\": false, \"stopReason\": \"biome check failed:\\n%s\"}' \"$(echo \"$output\" | head -30)\"; else echo '{}'; fi; else echo '{}'; fi ;; *) echo '{}' ;; esac; }", "timeout": 60, "statusMessage": "Running biome check..." }, { "type": "command", "command": "jq -r '.tool_input.command' | { read -r cmd; case \"$cmd\" in git\\ commit*) echo '{\"hookSpecificInstructions\": \"This is a git commit. Check for documentation drift: run git diff --cached --name-only to see staged files, then verify: (1) game domain/interactor/controller/presenter changes have README.md, CLAUDE.md Commands, docs/games.md, docs/manual/ updates; (2) Web API route changes have docs/architecture.md, api/openapi.yaml updates; (3) frontend page/component changes have frontend/CLAUDE.md i18n list updates; (4) new ADR files have docs/adr/README.md index updates; (5) controller schema changes have api/openapi.yaml updates. Block if required docs are missing from staged files.\"}' ;; *) echo '{}' ;; esac; }", "timeout": 10, "statusMessage": "Checking for documentation drift..." } ] } ], "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; case \"$f\" in *.go) goimports -w \"$f\" 2>/dev/null ;; esac; } || true", "statusMessage": "Running goimports..." }, { "type": "command", "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; case \"$f\" in *.ts|*.tsx) cd frontend && bunx biome check --write \"$f\" 2>/dev/null ;; esac; } || true", "statusMessage": "Running biome check..." } ] } ] } } # Gemini Code Assist Review Style Guide コードレビューを行う際は、コメントの視認性を高めるため、以下のルールに厳密に従ってください。 1. **バッジ(Badge)によるプレフィックスの義務化** すべてのレビューコメント(インラインコメントおよび全体サマリー)の先頭には、Shields.ioを利用した以下のMarkdownバッジを**必ず**挿入してください。指摘の重要度や性質に合わせて、最も適切なバッジを1つ選んでください。 - 🚨 致命的なバグや修正必須の指摘(赤色): `![MUST](https://img.shields.io/badge/MUST-red)` - 💡 強い推奨やパフォーマンス改善の提案(オレンジ色): `![WANT](https://img.shields.io/badge/WANT-orange)` - 📝 軽微な指摘やタイポ、リファクタリング提案(黄色): `![NITS](https://img.shields.io/badge/NITS-yellow)` - ✨ 素晴らしいコードや肯定的な評価(緑色): `![GOOD](https://img.shields.io/badge/GOOD-success)` - ❓ 単なる情報共有や質問(青色): `![INFO](https://img.shields.io/badge/INFO-blue)` 2. **ポジティブなコメントの分離(ノイズ削減)** インラインコメント(コード行に対するコメント)には、原則として `MUST`, `WANT`, `NITS` のいずれかのバッジを使用し、修正や改善が必要な箇所のみを指摘してください。 `GOOD` バッジを使用するような称賛や肯定的なコメントは、インラインには書かず、Pull Request全体のサマリーコメント(全体レビュー)の中にまとめて記載してください。 name: CI on: push: branches: [ develop, master ] pull_request: branches: [ develop ] jobs: lint-backend: name: Backend Lint (golangci-lint) runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '1.26' - name: Run golangci-lint uses: golangci/golangci-lint-action@v9 with: args: --build-tags test test-backend: name: Backend Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '1.26' - name: Download dependencies run: go mod download - name: Run backend tests with coverage run: go test -tags test -coverprofile=coverage.out -covermode=atomic ./... - name: Upload backend coverage to Codecov uses: codecov/codecov-action@v6 with: files: coverage.out flags: backend token: ${{ secrets.CODECOV_TOKEN }} lint-frontend: name: Frontend Lint (Biome) runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.10' - name: Install dependencies working-directory: frontend run: bun install --frozen-lockfile - name: Run Biome check working-directory: frontend run: bun run check test-frontend: name: Frontend Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.10' - name: Install dependencies working-directory: frontend run: bun install --frozen-lockfile - name: Run frontend tests with coverage working-directory: frontend run: bun run test:coverage - name: Upload frontend coverage to Codecov uses: codecov/codecov-action@v6 with: files: frontend/coverage/lcov.info flags: frontend token: ${{ secrets.CODECOV_TOKEN }} test-e2e: name: E2E Tests (Playwright) runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '1.26' - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.10' - name: Install frontend dependencies working-directory: frontend run: bun install --frozen-lockfile - name: Build frontend working-directory: frontend run: bun run build - name: Install Playwright browsers working-directory: frontend run: bunx playwright install --with-deps chromium - name: Run E2E tests working-directory: frontend run: bunx playwright test - name: Upload Playwright report uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: name: playwright-report path: frontend/playwright-report/ retention-days: 14 name: Claude Code PR Review on: pull_request: types: [opened] branches-ignore: - master issue_comment: types: [created] jobs: claude-review: runs-on: ubuntu-latest if: > github.event_name == 'pull_request' || (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) permissions: contents: write pull-requests: write issues: write steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 - name: Run Claude Code Action uses: anthropics/claude-code-action@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} name: Cloudflare Workers Build on: workflow_dispatch: push: branches: [develop] paths: - 'cmd/workers/**' - '.github/workflows/cloudflare-workers-build.yml' - 'Makefile' - 'internal/**' pull_request: branches: [develop] paths: - 'cmd/workers/**' - '.github/workflows/cloudflare-workers-build.yml' - 'Makefile' - 'internal/**' env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: tinygo-build: runs-on: ubuntu-latest strategy: matrix: worker: [casino, classic, solo] steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: '1.25' - uses: acifani/setup-tinygo@v2 with: tinygo-version: '0.40.1' - name: Install wasm-opt run: sudo apt-get install -y binaryen - name: Download Go modules env: GOTOOLCHAIN: local run: go mod download - name: Build and optimize env: GOTOOLCHAIN: local run: make build-worker-${{ matrix.worker }} - name: Check size limit env: WORKER_NAME: ${{ matrix.worker }} run: | WASM="workers/${WORKER_NAME}/build/app.wasm" RAW=$(stat -c%s "$WASM") GZIP=$(gzip -c "$WASM" | wc -c) echo "## ${WORKER_NAME} Worker" >> "$GITHUB_STEP_SUMMARY" echo "| Metric | Size |" >> "$GITHUB_STEP_SUMMARY" echo "|--------|------|" >> "$GITHUB_STEP_SUMMARY" echo "| Raw | $(echo "$RAW" | awk '{printf "%.2f MB", $1/1024/1024}') |" >> "$GITHUB_STEP_SUMMARY" echo "| gzip | $(echo "$GZIP" | awk '{printf "%.2f KB", $1/1024}') |" >> "$GITHUB_STEP_SUMMARY" if [ "$GZIP" -le 1048576 ]; then echo "Fits in free tier (< 1 MB)" >> "$GITHUB_STEP_SUMMARY" else echo "EXCEEDS free tier limit (1 MB)" >> "$GITHUB_STEP_SUMMARY" exit 1 fi - uses: actions/upload-artifact@v7 with: name: wasm-${{ matrix.worker }} path: workers/${{ matrix.worker }}/build/*.wasm # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ develop ] pull_request: # The branches below must be a subset of the branches above branches: [ develop ] schedule: - cron: '37 4 * * 0' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: include: - language: go build-mode: autobuild - language: javascript-typescript build-mode: none steps: - name: Checkout repository uses: actions/checkout@v6 - name: Set up Go if: matrix.language == 'go' uses: actions/setup-go@v6 with: go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 name: Deploy to Cloudflare on: push: branches: [develop, master] paths: - 'cmd/workers/**' - 'internal/**' - 'frontend/**' - 'Makefile' - 'workers/**' - '.github/workflows/deploy-cloudflare.yml' workflow_dispatch: inputs: environment: description: 'Deployment environment' required: true default: 'staging' type: choice options: - staging - production concurrency: group: deploy-cloudflare-${{ github.ref }} cancel-in-progress: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: deploy-workers: name: Deploy Workers (${{ matrix.worker }}) runs-on: ubuntu-latest strategy: matrix: worker: [casino, classic, solo] environment: name: ${{ github.ref == 'refs/heads/master' && 'production' || 'staging' }} steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: '1.25' - uses: acifani/setup-tinygo@v2 with: tinygo-version: '0.40.1' - name: Install wasm-opt run: sudo apt-get install -y binaryen - name: Download Go modules env: GOTOOLCHAIN: local run: go mod download - name: Build worker env: GOTOOLCHAIN: local WORKER_NAME: ${{ matrix.worker }} run: make "build-worker-${WORKER_NAME}" - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.10' - name: Deploy to Cloudflare Workers env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} WORKER_NAME: ${{ matrix.worker }} IS_PRODUCTION: ${{ github.ref == 'refs/heads/master' && 'true' || 'false' }} CORS_ORIGINS_PROD: ${{ vars.CORS_ALLOWED_ORIGINS_PROD }} CORS_ORIGINS_STAGING: ${{ vars.CORS_ALLOWED_ORIGINS_STAGING }} run: | ENV_FLAG="" CORS_ORIGINS="$CORS_ORIGINS_PROD" if [ "$IS_PRODUCTION" != "true" ]; then ENV_FLAG="--env staging" CORS_ORIGINS="$CORS_ORIGINS_STAGING" fi cd "workers/${WORKER_NAME}" bunx wrangler deploy \ $ENV_FLAG \ --var "CORS_ALLOWED_ORIGINS:${CORS_ORIGINS}" deploy-pages: name: Deploy Frontend (Pages) runs-on: ubuntu-latest needs: deploy-workers environment: name: ${{ github.ref == 'refs/heads/master' && 'production' || 'staging' }} steps: - uses: actions/checkout@v6 - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.10' - name: Install dependencies working-directory: frontend run: bun install --frozen-lockfile - name: Build frontend working-directory: frontend env: VITE_WORKER_CASINO_URL: ${{ github.ref == 'refs/heads/master' && vars.WORKER_CASINO_URL || vars.WORKER_CASINO_STAGING_URL }} VITE_WORKER_CLASSIC_URL: ${{ github.ref == 'refs/heads/master' && vars.WORKER_CLASSIC_URL || vars.WORKER_CLASSIC_STAGING_URL }} VITE_WORKER_SOLO_URL: ${{ github.ref == 'refs/heads/master' && vars.WORKER_SOLO_URL || vars.WORKER_SOLO_STAGING_URL }} run: bun run build - name: Deploy to Cloudflare Pages env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} PAGES_PROJECT: ${{ github.ref == 'refs/heads/master' && 'go-trumpcards' || 'go-trumpcards-staging' }} run: bunx wrangler pages deploy public --project-name "$PAGES_PROJECT" --commit-dirty=true name: Deploy Docs to GitHub Pages on: push: branches: - master # Allow only one concurrent deployment, skipping runs queued between the # run in-progress and latest queued. However, do NOT cancel in-progress runs # as we want to allow these production deployments to complete. concurrency: group: pages cancel-in-progress: false jobs: build: name: Build Documentation runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '1.26' - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: '24' - name: Set up Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.10' - name: Install Go doc tools run: go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest - name: Install pandoc run: sudo apt-get update && sudo apt-get install -y pandoc - name: Install frontend dependencies working-directory: frontend run: bun install --frozen-lockfile - name: Generate Go docs (Markdown) run: | mkdir -p _site/go for pkg in $(go list ./internal/...); do # Convert package path to directory structure rel="${pkg#github.com/yuta-yoshinaga/go_trumpcards/}" dir="_site/go/${rel}" mkdir -p "$dir" gomarkdoc "$pkg" > "$dir/index.md" 2>/dev/null || true done - name: Convert Go docs to HTML run: | find _site/go -name '*.md' | while read -r mdfile; do htmlfile="${mdfile%.md}.html" pandoc "$mdfile" \ --standalone \ --metadata title="$(basename "$(dirname "$mdfile")")" \ --css="https://cdn.jsdelivr.net/npm/water.css@2/out/water.min.css" \ -o "$htmlfile" rm "$mdfile" done - name: Generate Go package index run: | cat > _site/go/index.html <<'GOINDEX' Go API Documentation

Go API Documentation

← Back to top

    GOINDEX find _site/go -name 'index.html' -not -path '_site/go/index.html' | sort | while read -r f; do rel="${f#_site/go/}" dir="$(dirname "$rel")" echo "
  • $dir
  • " >> _site/go/index.html done cat >> _site/go/index.html <<'GOINDEX'
GOINDEX - name: Generate TypeScript docs working-directory: frontend run: bunx typedoc - name: Copy TypeScript docs run: | mkdir -p _site/ts cp -r frontend/docs/* _site/ts/ - name: Run repomix run: bunx repomix --output repomix-output.txt --style xml --compress --remove-empty-lines - name: Copy repomix output run: cp repomix-output.txt _site/repomix-output.txt - name: Copy landing page run: cp docs/pages/index.html _site/index.html - name: Upload pages artifact uses: actions/upload-pages-artifact@v4 with: path: _site deploy: name: Deploy to GitHub Pages needs: build runs-on: ubuntu-latest permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v5
name: Bump version on: push: branches: - master jobs: build: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Bump version and push tag id: tag_version uses: mathieudutour/github-tag-action@v6.2 with: github_token: ${{ github.token }} - name: Fetch new tag run: git fetch --tags - uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Release with GoReleaser uses: goreleaser/goreleaser-action@v7 with: version: "~> v2" args: release --clean env: GITHUB_TOKEN: ${{ github.token }} GORELEASER_CURRENT_TAG: ${{ steps.tag_version.outputs.new_tag }} // Package api provides the embedded OpenAPI specification. package api import _ "embed" // OpenAPISpec contains the raw bytes of the OpenAPI 3.1.0 specification (openapi.yaml). // //go:embed openapi.yaml var OpenAPISpec []byte openapi: 3.1.0 info: title: go_trumpcards API description: | REST API for the go_trumpcards card game server. Each endpoint manages game state on the server using a session identifier (`sessionId`) supplied by the client. Sessions expire after 1 hour of inactivity and the server supports up to 10 000 concurrent sessions. version: 1.0.0 servers: - url: / description: Current server (resolves to the host serving the Swagger UI page) tags: - name: blackjack description: BlackJack game - name: poker description: 5-card Draw Poker game - name: oldmaid description: Old Maid (Babanuki) game - name: daifugo description: Daifugo (大富豪) game - name: sevens description: Sevens (7並べ) game - name: doubt description: Doubt (ダウト) game - name: holdem description: Texas Hold'em game - name: omaha description: Omaha Hold'em game - name: shortdeck description: Short Deck (6+ Hold'em) game - name: hearts description: Hearts game - name: memory description: Memory (神経衰弱) game - name: klondike description: Klondike (ソリティア) game - name: freecell description: FreeCell (フリーセル) game - name: baccarat description: Baccarat (バカラ) game - name: spades description: Spades (スペード) game - name: crazyeights description: Crazy Eights (クレイジーエイト) game - name: ginrummy description: Gin Rummy (ジンラミー) game - name: canasta description: Canasta (カナスタ) game - name: pinochle description: Pinochle (ピノクル) game - name: spider description: Spider Solitaire (スパイダーソリティア) game - name: napoleon description: Napoleon (ナポレオン) game - name: indianpoker description: Indian Poker (インディアンポーカー) game - name: videopoker description: "Video Poker (Jacks or Better)" - name: deuceswild description: "Deuces Wild (デューシーズワイルド) - Video Poker variant" - name: jokerpoker description: "Joker Poker (ジョーカーポーカー) - Video Poker variant" - name: euchre description: Euchre (ユーカー) game - name: pyramid description: Pyramid (ピラミッド) game - name: tripeaks description: TriPeaks (トリピークス) game - name: golf description: Golf Solitaire (ゴルフ) game - name: cribbage description: Cribbage (クリベッジ) game - name: threecard description: Three Card Poker (スリーカードポーカー) game - name: ohhell description: Oh Hell (オー・ヘル) game - name: bridge description: Contract Bridge (コントラクトブリッジ) game - name: pineapple description: Pineapple Poker (パイナップルポーカー) game - name: speed description: Speed (スピード) game - name: gofish description: Go Fish (ゴーフィッシュ) game - name: pigtail description: Pig's Tail (ぶたのしっぽ) game - name: sevencardstud description: Seven Card Stud (セブンカード・スタッド) game - name: clocksolitaire description: Clock Solitaire (クロックソリティア) game paths: /blackjack/exec: post: tags: - blackjack summary: Execute a BlackJack game command description: | Send a command to advance the BlackJack game session identified by `sessionId`. The game proceeds through five phases: 1. **Bet** (`phase=1`) — Player places an initial bet 2. **Deal** (`phase=2`) — Two cards dealt to each; check if dealer shows Ace 3. **Insurance** (`phase=3`) — Player accepts/declines insurance (only if dealer shows Ace) 4. **Action** (`phase=4`) — Hit / Stand / Double Down / Split / Surrender 5. **End** (`phase=5`) — Dealer plays, judgment, payout 6. **Early Surrender** (`phase=6`) — Player decides whether to surrender before dealer BJ check (only when surrender rule = Early) | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place initial bet (requires `amount`) | | `hit` | `h` | Draw an additional card | | `stand` | `s` | End the player turn; trigger dealer play and judgment | | `doubledown` | `d` | Double bet, draw one card, auto-stand | | `split` | `sp` | Split a pair into two hands | | `insurance` | `i` | Accept insurance (costs half of original bet) | | `declineinsurance` | `di` | Decline insurance | | `surrender` | `sur` | Surrender first 2 cards and receive half bet back | | `togglehint` | — | Toggle basic strategy hint on/off | | `setdeckcount` | `sd` | Set shoe deck count (requires `amount`: 1/2/4/6/8) | | `earlysurrender` | `es` | Surrender before dealer BJ check (early surrender phase only) | | `declineearlysurrender` | `des` | Decline early surrender and continue normal play | | `setsurrenderrule` | `ssr` | Set surrender rule (requires `amount`: 0=Late/1=Early/2=No surrender) | | `togglesoft17` | `soft17` | Toggle dealer soft-17 rule (H17 ↔ S17) | | `togglecounting` | `counting` | Toggle card counting display on/off | | `toggledas` | `das` | Toggle double-after-split (DAS) on/off | | `setcountingsystem` | `scs` | Set counting system (requires `amount`: 0=Hi-Lo/1=KO/2=Zen Count/3=Omega II) | | `quit` | `q` | End the session | operationId: blackjackExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BlackJackRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bet: summary: Place a bet value: command: bet amount: 100 sessionId: my-session-001 hit: summary: Draw a card value: command: hit sessionId: my-session-001 stand: summary: Stand and end turn value: command: stand sessionId: my-session-001 doubledown: summary: Double down value: command: doubledown sessionId: my-session-001 split: summary: Split a pair value: command: split sessionId: my-session-001 insurance: summary: Accept insurance value: command: insurance sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/BlackJackResponse' examples: inProgress: summary: Game in progress (dealer hole card is hidden) value: dealer: score: 0 cards: - design: SPADE value: 10 chips: 1000 player: chips: 900 hands: - score: 18 cards: - design: CLOVER value: 8 - design: DIAMOND value: 10 bet: 100 stood: false doubled: false busted: false isBlackJack: false canSplit: false phase: 4 currentHandIdx: 0 insuranceBet: 0 insuranceAvailable: false message: "" playerWins: summary: Game over — player wins (all dealer cards revealed) value: dealer: score: 22 cards: - design: SPADE value: 10 - design: HEART value: 5 - design: CLOVER value: 7 chips: 900 player: chips: 1200 hands: - score: 20 cards: - design: DIAMOND value: 10 - design: HEART value: 10 bet: 100 stood: true doubled: false busted: false isBlackJack: false canSplit: false phase: 5 currentHandIdx: 0 insuranceBet: 0 insuranceAvailable: false message: You are the winner. quit: summary: Quit command response value: dealer: chips: 0 player: chips: 0 phase: 1 currentHandIdx: 0 insuranceBet: 0 insuranceAvailable: false message: bye. '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/BlackJackResponse' examples: paramError: summary: Missing required parameter value: dealer: chips: 0 player: chips: 0 phase: 1 currentHandIdx: 0 insuranceBet: 0 insuranceAvailable: false message: param error. /poker/exec: post: tags: - poker summary: Execute a 5-card Draw Poker game command description: | Send a command to advance the Poker game session identified by `sessionId`. The game supports 1 human + 1-3 CPU players. The game proceeds through five phases: | Phase | Value | Description | |------------|-------|-------------| | Init | `0` | Before any cards are dealt | | Deal | `1` | Cards dealt; first betting round | | Exchange | `2` | Player may exchange cards | | SecondBet | `3` | Second betting round (after exchange) | | End | `4` | Showdown; final hands revealed | | Command | Aliases | Description | |------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place a bet (use `amount`) | | `call` | `c` | Match the current bet | | `raise` | `ra` | Raise (use `amount`) | | `check` | `ck` | Pass without betting (only when no outstanding bet) | | `fold` | `f` | Forfeit the pot | | `allin` | `a` | Go all-in (bet all remaining chips) | | `exchange` | `e` | Exchange selected cards (use `indices` to specify which) | | `stand` | `s` | Keep current hand (no exchange) | | `odds` | `o` | Calculate draw odds for selected cards (read-only, exchange phase only) | | `quit` | `q` | End the session | operationId: pokerExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PokerRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 resetWithConfig: summary: Start a new game with 2 CPUs and 1 joker value: command: reset cpuCount: 2 jokerCount: 1 sessionId: my-session-001 bet: summary: Place a bet of 20 value: command: bet amount: 20 sessionId: my-session-001 call: summary: Call the current bet value: command: call sessionId: my-session-001 allin: summary: Go all-in value: command: allin sessionId: my-session-001 exchange: summary: Exchange cards at positions 0 and 2 value: command: exchange indices: [0, 2] sessionId: my-session-001 stand: summary: Stand (no exchange) value: command: stand sessionId: my-session-001 odds: summary: Calculate draw odds for cards at positions 0 and 2 value: command: odds indices: [0, 2] sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/PokerResponse' examples: dealPhase: summary: Cards dealt — first betting round (phase 1) value: players: - id: 0 isHuman: true cards: - design: SPADE value: 5 - design: HEART value: 5 - design: CLOVER value: 9 - design: DIAMOND value: 9 - design: SPADE value: 2 chips: 990 currentBet: 0 folded: false allIn: false handRank: 2 handName: Two Pair exchangeCount: -1 playStyleName: "" - id: 1 isHuman: false cards: [] chips: 990 currentBet: 0 folded: false allIn: false handRank: 0 handName: "" exchangeCount: -1 playStyleName: Balanced pot: 20 sidePots: [] dealerIdx: 0 currentTurn: 0 phase: 1 gameEndFlag: false lastBet: 0 minRaise: 10 ante: 10 jokerCount: 0 roundResults: [] cpuActions: [] cpuExchanges: [] message: "" endPhase: summary: Showdown (phase 4) value: players: - id: 0 isHuman: true cards: - design: SPADE value: 5 - design: HEART value: 5 - design: CLOVER value: 9 - design: DIAMOND value: 9 - design: SPADE value: 2 chips: 1040 currentBet: 0 folded: false allIn: false handRank: 2 handName: Two Pair exchangeCount: 0 playStyleName: "" - id: 1 isHuman: false cards: - design: SPADE value: 3 - design: HEART value: 3 - design: CLOVER value: 7 - design: DIAMOND value: 10 - design: SPADE value: 1 chips: 960 currentBet: 0 folded: false allIn: false handRank: 1 handName: One Pair exchangeCount: 3 playStyleName: Balanced pot: 0 sidePots: [] dealerIdx: 0 currentTurn: 0 phase: 4 gameEndFlag: false lastBet: 0 minRaise: 10 ante: 10 jokerCount: 0 roundResults: - playerIdx: 0 handRank: 2 handName: Two Pair wonAmount: 40 cpuActions: [] cpuExchanges: [] message: You are the winner. '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/PokerResponse' examples: paramError: value: players: - id: 0 isHuman: true cards: [] chips: 0 currentBet: 0 folded: false allIn: false handRank: 0 handName: "" exchangeCount: -1 playStyleName: "" pot: 0 sidePots: [] dealerIdx: 0 currentTurn: 0 phase: 0 gameEndFlag: false lastBet: 0 minRaise: 0 ante: 0 jokerCount: 0 roundResults: [] cpuActions: [] cpuExchanges: [] message: param error. /oldmaid/exec: post: tags: - oldmaid summary: Execute an Old Maid (Babanuki) game command description: | Send a command to advance the Old Maid game session identified by `sessionId`. The game has 4 players (player 0 = human, players 1–3 = CPU). | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `draw` | `d` | Draw a card from the next target player; use `drawIdx` to specify which card slot to draw from (omit for random) | | `quit` | `q` | End the session | operationId: oldmaidExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OldMaidRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 draw: summary: Draw the card at index 1 from the target player value: command: draw drawIdx: 1 sessionId: my-session-001 drawRandom: summary: Draw a random card from the target player value: command: draw sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/OldMaidResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/OldMaidResponse' examples: paramError: value: players: [] currentTurn: 0 nextDrawTargetIdx: 0 gameEndFlag: false loserIdx: -1 lastDrawPlayerIdx: 0 lastDrawFromIdx: 0 lastDrawCard: null lastDiscardedPairs: 0 hasDrawn: false cpuActions: [] message: param error. /daifugo/exec: post: tags: - daifugo summary: Execute a Daifugo (大富豪) game command description: | Send a command to advance the Daifugo game session identified by `sessionId`. The game has 4 players (player 0 = human, players 1–3 = CPU). Final player ranks: 1 = 大富豪, 2 = 富豪, 3 = 平民, 4 = 大貧民. | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `play` | `p` | Play cards specified by `indices`; pass with an empty array | | `sort` | | Sort human player's hand (optional `sortMode` param) | | `quit` | `q` | End the session | operationId: daifugoExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DaifugoRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 play: summary: Play cards at hand positions 0 and 2 value: command: play indices: [0, 2] sessionId: my-session-001 pass: summary: Pass (empty indices) value: command: play indices: [] sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/DaifugoResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/DaifugoResponse' examples: paramError: value: players: [] currentTurn: 0 tableCards: [] lastPlayPlayerIdx: 0 gameEndFlag: false cpuActions: [] humanAction: null message: param error. /sevens/exec: post: tags: - sevens summary: Execute a Sevens (7並べ) game command description: | Send a command to advance the Sevens game session identified by `sessionId`. The game has 4 players (player 0 = human, players 1–3 = CPU). Each suit builds outward from 7. The board state is represented by `tableMinVals` and `tableMaxVals` — arrays of 5 integers indexed by suit (0 = unused, 1 = SPADE, 2 = CLOVER, 3 = HEART, 4 = DIAMOND). Players have a configurable number of passes (`maxPasses`, default 5, 0 = unlimited). | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `play` | `p` | Play the card at hand position `index`; pass with `index: -1` | | `quit` | `q` | End the session | operationId: sevensExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SevensRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 playCard: summary: Play the card at hand index 3 value: command: play index: 3 sessionId: my-session-001 pass: summary: Pass (index -1) value: command: play index: -1 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/SevensResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/SevensResponse' examples: paramError: value: players: [] currentTurn: 0 tableMinVals: [0, 0, 0, 0, 0] tableMaxVals: [0, 0, 0, 0, 0] gameEndFlag: false cpuActions: [] humanAction: null message: param error. /doubt/exec: post: tags: - doubt summary: Execute a Doubt (ダウト) game command description: | Send a command to advance the Doubt game session identified by `sessionId`. The game has 4 players (player 0 = human, players 1–3 = CPU). Each turn, the active player places cards face-down and declares a card value (which may be a bluff). After a card is played, other players can declare "doubt" (`d`/`doubt`) or pass (`s`/`skip`). | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `play` | `p` | Play `cardIndices` cards declaring `claimedValue` | | `doubt` | `d` | Declare doubt; supply `doubterIndices` (human = 0 + any CPU doubters) | | `skip` | `s` | Pass on doubting; `doubterIndices` is ignored (server uses its own state) | | `quit` | `q` | End the session | operationId: doubtExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DoubtRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 play: summary: Play cards at hand indices 0 and 2, declare value 3 value: command: play cardIndices: [0, 2] claimedValue: 3 sessionId: my-session-001 doubt: summary: Human doubts (with CPU 2 also doubting) value: command: doubt doubterIndices: [0, 2] sessionId: my-session-001 skip: summary: Human passes (doubterIndices is not required for skip) value: command: skip sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/DoubtResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/DoubtResponse' examples: paramError: value: players: [] currentTurn: 0 phase: 0 tableCardCount: 0 lastAction: null cpuDoubters: [] cpuActions: [] humanAction: null lastDoubtResult: null gameEndFlag: false winnerIdx: -1 message: param error. /holdem/exec: post: tags: - holdem summary: Execute a Texas Hold'em game command description: | Send a command to advance the Texas Hold'em game session identified by `sessionId`. The game supports configurable table sizes: 4-max (default, 1 human + 3 CPU), 6-max (1 human + 5 CPU), or 9-max (1 human + 8 CPU). Each CPU has a distinct play style. The game proceeds through eight phases: | Phase | Value | Description | |------------|-------|-------------| | Init | `0` | Before any cards are dealt | | PreFlop | `1` | Two hole cards dealt; first betting round | | Flop | `2` | Three community cards revealed | | Turn | `3` | Fourth community card revealed | | River | `4` | Fifth community card revealed | | Showdown | `5` | Hands evaluated; winners determined | | End | `6` | Round complete; results displayed | | Rebuy | `7` | Rebuy/addon decision phase | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `fold` | `f` | Fold and forfeit the pot | | `check` | `ck` | Pass without betting (only when no outstanding bet) | | `call` | `c` | Match the current bet | | `bet` | `b` | Place a bet (use `amount`) | | `raise` | `ra` | Raise by a specified amount on top of calling (use `amount`) | | `allin` | `a` | Go all-in with all remaining chips | | `rebuy` | `rb` | Buy back chips during rebuy period | | `skiprebuy` | `sr` | Skip rebuy opportunity | | `addon` | `ad` | Add chips (one-time addon purchase) | | `skipaddon` | `sa` | Skip addon opportunity | | `quit` | `q` | End the session | operationId: holdemExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/HoldemRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 resetCustomBlinds: summary: Start with custom blinds value: command: reset smallBlind: 10 bigBlind: 20 sessionId: my-session-001 fold: summary: Fold value: command: fold sessionId: my-session-001 call: summary: Call value: command: call sessionId: my-session-001 bet: summary: Place a bet of 40 value: command: bet amount: 40 sessionId: my-session-001 raise: summary: Raise by 80 on top of calling value: command: raise amount: 80 sessionId: my-session-001 allin: summary: Go all-in value: command: allin sessionId: my-session-001 rebuy: summary: Accept rebuy value: command: rebuy sessionId: my-session-001 skipaddon: summary: Skip addon value: command: skipaddon sessionId: my-session-001 resetWithRebuy: summary: Start with rebuy/addon enabled value: command: reset rebuyEnabled: true addonEnabled: true sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/HoldemResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/HoldemResponse' examples: paramError: value: players: [] communityCards: [] pot: 0 sidePots: [] dealerIdx: 0 currentTurn: 0 phase: 0 gameEndFlag: false lastBet: 0 minRaise: 0 roundResults: [] cpuActions: [] message: param error. /omaha/exec: post: tags: - omaha summary: Execute an Omaha Hold'em game command description: | Send a command to advance the Omaha Hold'em game session identified by `sessionId`. Omaha is a variant of Texas Hold'em where each player receives 4 hole cards and must use exactly 2 hole cards + 3 community cards to form the best 5-card hand. The game uses the same phases, commands, and configuration options as Texas Hold'em. See the `/holdem/exec` endpoint for full documentation. operationId: omahaExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/HoldemRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 fold: summary: Fold value: command: fold sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/HoldemResponse' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/HoldemResponse' /shortdeck/exec: post: tags: - shortdeck summary: Execute a Short Deck (6+ Hold'em) game command description: | Send a command to advance the Short Deck Hold'em game session identified by `sessionId`. Short Deck (6+ Hold'em) is a variant of Texas Hold'em that uses a 36-card deck (2–5 of each suit removed). Flush beats Full House, and the lowest straight is A-6-7-8-9. The game uses the same phases, commands, and configuration options as Texas Hold'em. See the `/holdem/exec` endpoint for full documentation. operationId: shortdeckExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/HoldemRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 fold: summary: Fold value: command: fold sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/HoldemResponse' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/HoldemResponse' /hearts/exec: post: tags: - hearts summary: Execute a Hearts game command description: | Send a command to advance the Hearts game session identified by `sessionId`. Hearts is a 4-player trick-taking card game (1 human + 3 CPU). Players avoid taking hearts (1 point each) and Q♠ (13 points). Game ends when any player reaches 100+ points; lowest score wins. The game proceeds through these phases: | Phase | Value | Description | |-------------|-------|-------------| | Pass | `0` | Pass 3 cards to another player | | Play | `1` | Play a card to the current trick | | TrickEnd | `2` | Trick complete; view result | | RoundEnd | `3` | Round complete; view scores | | GameEnd | `4` | Game over; final standings | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `pass` | — | Pass 3 cards (requires `cardIndices`) | | `play` | `p` | Play a card (requires `cardIndex`) | | `next` | `n` | Advance to next trick | | `nextround` | `nr` | Advance to next round | | `setdifficulty` | `sd` | Set CPU difficulty (requires `config.difficulty`: 0=Easy, 1=Normal, 2=Hard) | | `setlimit` | `sl` | Set point limit (requires `config.pointLimit`) | | `log` | `l` | View action log | | `quit` | `q` | End the session | operationId: heartsExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/HeartsRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 pass: summary: Pass 3 cards value: command: pass cardIndices: [0, 3, 7] sessionId: my-session-001 play: summary: Play a card value: command: play cardIndex: 2 sessionId: my-session-001 next: summary: Next trick value: command: next sessionId: my-session-001 nextround: summary: Next round value: command: nextround sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/HeartsResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/HeartsResponse' examples: paramError: value: players: [] phase: 0 roundNumber: 0 trickNumber: 0 currentPlayerIdx: 0 currentTrick: [] heartsBroken: false passDirection: 0 gameEndFlag: false winnerIdx: -1 message: param error. /memory/exec: post: tags: - memory summary: Execute a Memory game command description: | Send a command to advance the Memory (神経衰弱) game session identified by `sessionId`. Memory is a 4-player concentration card game (1 human + 3 CPU). 52 cards are laid face-down; each turn a player flips 2 cards. If they share the same rank the player takes the pair and gets another turn; otherwise the cards are flipped back and play passes to the next player. Game ends when all 26 pairs are taken; most pairs wins. The game proceeds through these phases: | Phase | Value | Description | |-------------|-------|-------------| | Flip1 | `0` | First card flip | | Flip2 | `1` | Second card flip | | Result | `2` | Flip result (match or mismatch) | | GameEnd | `3` | Game over; final standings | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game (accepts optional `config.cpuDifficulty`) | | `flip` | `f` | Flip a card (requires `position`) | | `next` | `n` | Advance after viewing result | | `log` | `l` | View action log | operationId: memoryExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MemoryRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 flip: summary: Flip a card value: command: flip position: 5 sessionId: my-session-001 next: summary: Advance after result value: command: next sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/MemoryResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/MemoryResponse' examples: paramError: value: players: [] phase: 0 board: [] currentPlayerIdx: 0 firstFlipPos: -1 secondFlipPos: -1 lastMatchResult: false turnNumber: 0 gameEndFlag: false winnerIdx: -1 message: param error. config: cpuDifficulty: 1 /klondike/exec: post: tags: - klondike summary: Execute a Klondike game command description: | Send a command to advance the Klondike (ソリティア) game session identified by `sessionId`. Klondike is a single-player solitaire card game using a standard 52-card deck. 7 tableau columns with cascading face-down/face-up cards, a stock pile, a waste pile, and 4 foundation piles (one per suit). Build foundations from Ace to King by suit; build tableau columns in descending rank with alternating colors. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All foundations completed; player wins | | GameOver | `2` | No more moves available; game over | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game (optionally with `config`) | | `draw` | `d` | Draw card(s) from stock to waste (1 or 3 depending on draw mode) | | `move` | `m` | Move card(s) between zones (requires `from` and `to`) | | `giveup` | `g` | Give up the current game | | `hint` | `h` | Get a hint for the next move | | `autocomplete` | `ac` | Auto-complete remaining moves to foundation | | `undo` | `u` | Undo the last action | | `undo_n` | | Undo N actions (requires `n` parameter) | | `log` | `l` | View action log | operationId: klondikeExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/KlondikeRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 draw: summary: Draw from stock value: command: draw sessionId: my-session-001 move: summary: Move a card value: command: move from: zone: waste to: zone: tableau col: 3 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/KlondikeResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/KlondikeResponse' examples: paramError: value: tableau: [] stockCount: 0 waste: [] foundation: [] phase: 0 moveCount: 0 message: param error. /freecell/exec: post: tags: - freecell summary: Execute a FreeCell game command description: | Send a command to advance the FreeCell (フリーセル) game session identified by `sessionId`. FreeCell is a single-player solitaire card game using a standard 52-card deck. 8 tableau columns (first 4 have 7 cards, last 4 have 6 cards), all dealt face-up. 4 free cells for temporary single-card storage. 4 foundation piles: build up by suit Ace to King. Tableau: descending rank, alternating colors; only King on empty column. Supermove: max movable cards = (1 + emptyFreeCells) x 2^(emptyTableauCols). The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All foundations completed; player wins | | GameOver | `2` | No more moves available; game over | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `move` | `m` | Move card(s) between zones (requires `from` and `to`) | | `giveup` | `g` | Give up the current game | | `hint` | `h` | Get a hint for the next move | | `autocomplete` | `ac` | Auto-complete remaining moves to foundation | | `undo` | `u` | Undo the last action | | `undo_n` | | Undo N actions (requires `n` parameter) | | `log` | `l` | View action log | operationId: freecellExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/FreeCellRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 move: summary: Move a card value: command: move from: zone: tableau col: 0 cardIndex: 6 to: zone: freecell cell: 0 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/FreeCellResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/FreeCellResponse' examples: paramError: value: tableau: [] freeCells: [] foundation: [] phase: 0 moveCount: 0 canUndo: false message: param error. /baccarat/exec: post: tags: - baccarat summary: Execute a Baccarat game command description: | Send a command to advance the Baccarat (バカラ) game session identified by `sessionId`. Baccarat is a casino card game using a standard 52-card deck. Player bets on Player, Banker, or Tie, then cards are dealt automatically following baccarat third-card rules. Card points: A=1, 2-9=face value, 10/J/Q/K=0. Hand value is the ones digit of the sum. The game proceeds through these phases: | Phase | Value | Description | |-------|-------|-------------| | Bet | `1` | Place a bet (amount and type) | | End | `2` | Round complete; shows result and payout | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place a bet (requires `amount` and `betType`) | | `log` | `l` | View action log | operationId: baccaratExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BaccaratRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 betPlayer: summary: Bet 100 on Player value: command: bet amount: 100 betType: 0 sessionId: my-session-001 betBanker: summary: Bet 200 on Banker value: command: bet amount: 200 betType: 1 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/BaccaratResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/BaccaratResponse' examples: paramError: value: playerHand: [] bankerHand: [] playerHandValue: 0 bankerHandValue: 0 phase: 0 chips: 0 betAmount: 0 betType: 0 result: 0 payout: 0 message: param error. /spades/exec: post: tags: - spades summary: Execute a Spades game command description: | Send a command to advance the Spades (スペード) game session identified by `sessionId`. Spades is a 4-player trick-taking game where spades are always trump. Players bid how many tricks they expect to win, then play 13 tricks. Scoring: made bid = bid×10 + overtricks (bags); failed = -bid×10; nil bid (0) success = +100, fail = -100; every 10 bags = -100 penalty. First to 500 wins; drop below -200 and lose. The game proceeds through these phases: | Phase | Value | Description | |-----------|-------|-------------| | Bid | `0` | Each player declares expected trick wins | | Play | `1` | Trick play (follow suit; spades are trump) | | TrickEnd | `2` | Trick resolved; advance with `next` | | RoundEnd | `3` | All 13 tricks played; advance with `nextround` | | GameEnd | `4` | A player reached the point limit or fell below -200 | | Command | Aliases | Description | |-------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bid` | `b` | Place a bid (requires `bid` field, 0-13) | | `play` | `p` | Play a card (requires `cardIndex`) | | `next` | `n` | Advance to the next trick | | `nextround` | `nr` | Score the round and start the next | | `log` | `l` | View action log | operationId: spadesExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SpadesRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bid: summary: Bid 3 tricks value: command: bid bid: 3 sessionId: my-session-001 play: summary: Play card at index 2 value: command: play cardIndex: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/SpadesResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/SpadesResponse' /crazyeights/exec: post: tags: - crazyeights summary: Execute a Crazy Eights game command description: | Send a command to advance the Crazy Eights (クレイジーエイト) game session identified by `sessionId`. Crazy Eights is a 4-player shedding card game using a standard 52-card deck. Players match the top discard by suit or rank, or play an 8 (wild) and choose the next suit. Cannot play? Draw 1 card. Round ends when a player empties their hand; scoring: 8=50, J/Q/K=10, A=1, others=face value. Match ends at the point limit (default 200). The game proceeds through these phases: | Phase | Value | Description | |--------------|-------|-------------| | Play | `0` | Play a card or draw | | ChooseSuit | `1` | Choose suit after playing an 8 | | RoundEnd | `2` | A player emptied their hand; advance with `nextround` | | GameEnd | `3` | A player reached the point limit | | Command | Aliases | Description | |-------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `play` | `p` | Play a card (requires `cardIndex`) | | `draw` | `d` | Draw a card from the draw pile | | `suit` | `s` | Choose next suit after playing 8 (requires `suit`: 1-4) | | `nextround` | `nr` | Score the round and start the next | | `log` | `l` | View action log | operationId: crazyeightsExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CrazyEightsRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 play: summary: Play card at index 2 value: command: play cardIndex: 2 sessionId: my-session-001 draw: summary: Draw a card value: command: draw sessionId: my-session-001 suit: summary: Choose hearts as next suit value: command: suit suit: 3 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/CrazyEightsResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/CrazyEightsResponse' /ginrummy/exec: post: tags: - ginrummy summary: Execute a Gin Rummy game command description: | Send a command to advance the Gin Rummy (ジンラミー) game session identified by `sessionId`. Gin Rummy is a 2-player rummy card game using a standard 52-card deck. 10 cards are dealt to each player. Draw from stock or discard pile, then discard or knock. Melds are sets (3-4 same rank) and runs (3+ consecutive same suit). Knock when deadwood ≤ 10. Gin = 0 deadwood (25 bonus). Undercut = opponent has ≤ knocker's deadwood (25 bonus to opponent). First to reach point limit wins. The game proceeds through these phases: | Phase | Value | Description | |--------------|-------|-------------| | Draw | `0` | Draw a card from stock or discard pile | | Discard | `1` | Discard a card or knock | | Layoff | `2` | Opponent lays off cards on knocker's melds | | RoundEnd | `3` | Round scored; advance with `nextround` | | GameEnd | `4` | A player reached the point limit | | Command | Aliases | Description | |----------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `drawstock` | `ds` | Draw a card from the stock pile | | `drawdiscard` | `dd` | Draw the top card from the discard pile | | `discard` | `d` | Discard a card (requires `cardIndex`) | | `knock` | `k` | Knock (deadwood ≤ 10) (requires `cardIndex`) | | `layoff` | `lo` | Lay off cards on knocker's melds (requires `cardIndices`) | | `nextround` | `nr` | Score the round and start the next | | `log` | `l` | View action log | operationId: ginrummyExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/GinRummyRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 drawstock: summary: Draw from stock pile value: command: drawstock sessionId: my-session-001 drawdiscard: summary: Draw from discard pile value: command: drawdiscard sessionId: my-session-001 discard: summary: Discard card at index 2 value: command: discard cardIndex: 2 sessionId: my-session-001 knock: summary: Knock (discard card at index 2) value: command: knock cardIndex: 2 sessionId: my-session-001 layoff: summary: Lay off cards at indices 0 and 3 value: command: layoff cardIndices: [0, 3] sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/GinRummyResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/GinRummyResponse' /canasta/exec: post: tags: - canasta summary: Execute a Canasta game command description: | Send a command to advance the Canasta (カナスタ) game session identified by `sessionId`. Canasta is a 2-player rummy card game using 2 decks + 4 jokers (108 cards). 15 cards are dealt to each player. Wild cards (2s and Jokers) can substitute in melds. Canasta = 7+ card meld. First to reach point limit wins. The game proceeds through these phases: | Phase | Value | Description | |--------------|-------|-------------| | Draw | `0` | Draw from stock or pick up discard pile | | Meld | `1` | Meld cards or skip | | Discard | `2` | Discard a card or go out | | RoundEnd | `3` | Round scored; advance with `nextround` | | GameEnd | `4` | A player reached the point limit | | Command | Aliases | Description | |----------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `drawstock` | `ds` | Draw a card from the stock pile | | `drawdiscard` | `dd` | Pick up the discard pile (requires `naturalPairIndices`) | | `meld` | `m` | Meld cards (requires `meldGroups`) | | `skipmeld` | `sm` | Skip meld phase | | `discard` | `d` | Discard a card (requires `cardIndex`) | | `goout` | `go` | Go out (requires at least 1 canasta) | | `nextround` | `nr` | Score the round and start the next | | `log` | `l` | View action log | operationId: canastaExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CanastaRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 drawstock: summary: Draw from stock pile value: command: drawstock sessionId: my-session-001 drawdiscard: summary: Pick up discard pile with natural pair at indices 0 and 1 value: command: drawdiscard naturalPairIndices: [0, 1] sessionId: my-session-001 meld: summary: Meld cards at indices 0, 1, 2 value: command: meld meldGroups: [[0, 1, 2]] sessionId: my-session-001 discard: summary: Discard card at index 2 value: command: discard cardIndex: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/CanastaResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/CanastaResponse' /pinochle/exec: post: tags: - pinochle summary: Execute a Pinochle game command description: | Send a command to advance the Pinochle (ピノクル) game session identified by `sessionId`. Pinochle is a 4-player 2-team trick-taking card game with bidding and melding using a 48-card double deck (9,10,J,Q,K,A × 2 per suit). 12 cards dealt to each player. | Phase | Value | Description | |-----------|-------|-------------| | Bid | `0` | Players bid expected total points | | Trump | `1` | Highest bidder declares trump suit | | Meld | `2` | All players display melds | | Play | `3` | 12 tricks with must-follow/trump/win rules | | TrickEnd | `4` | Trick resolved | | RoundEnd | `5` | Round scored | | GameEnd | `6` | A team reached the point limit | **Commands:** | Command | Parameters | Description | |-------------|-------------------------|-------------| | `reset` | `config` (opt) | Start a new game | | `bid` | `bidAmount` | Place a bid | | `pass` | | Pass on bidding | | `trump` | `suit` | Declare trump suit (1-4) | | `meld` | | Confirm melds and proceed to play | | `play` | `cardIndex` | Play a card from hand | | `next` | | Proceed to next trick | | `nextround` | | Proceed to next round | | `hint` | | Get AI hint | | `log` | | Get action log | requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PinochleRequest' responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/PinochleResponse' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/PinochleResponse' /spider/exec: post: tags: - spider summary: Execute a Spider Solitaire game command description: | Send a command to advance the Spider Solitaire game session identified by `sessionId`. Spider Solitaire is a single-player solitaire game using 2 decks (104 cards) with 10 tableau columns. Move same-suit descending sequences between columns. Complete K-through-A same-suit sequences are automatically removed. 8 completed suits = game clear. Difficulty: 1/2/4 suits. | Phase | Value | Description | |-------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All 8 suits completed | | GameOver | `2` | Player gave up | | Command | Aliases | Description | |----------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `deal` | `d` | Deal 1 card to each column from stock | | `move` | `m` | Move cards between tableau columns | | `giveup` | `g` | Give up | | `hint` | `h` | Get a move suggestion | | `autocomplete` | `ac` | Auto-complete (when all face up) | | `undo` | `u` | Undo last move | | `undo_n` | | Undo N moves (requires `n` parameter) | | `log` | `l` | View action log | operationId: spiderExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SpiderRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 resetWithDifficulty: summary: Start a new game with 2-suit difficulty value: command: reset config: difficulty: 2 sessionId: my-session-001 deal: summary: Deal cards from stock value: command: deal sessionId: my-session-001 move: summary: Move card(s) from column 3, index 2 to column 5 value: command: move from: zone: tableau col: 3 cardIndex: 2 to: zone: tableau col: 5 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/SpiderResponse' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/SpiderResponse' /napoleon/exec: post: tags: - napoleon summary: Execute a Napoleon game command description: | Send a command to advance the Napoleon (ナポレオン) game session identified by `sessionId`. Napoleon is a 4-player trick-taking card game using 52 cards + 1 Joker (53 total). Players bid the number of picture cards (J/Q/K/A + Joker = 17 max) they expect to capture. The highest bidder becomes Napoleon, declares a trump suit, and names an adjutant card (hidden ally). Hidden teams: Napoleon's Army (Napoleon + adjutant holder) vs Allied Forces. Special cards: Joker (mighty -- strongest card), Spade 3 (joker killer -- beats mighty). Napoleon's team must capture >= bid number of picture cards to win. The game proceeds through these phases: | Phase | Value | Description | |-----------|-------|-------------| | Bid | `0` | Each player bids picture card count or passes | | Trump | `1` | Napoleon declares the trump suit | | Exchange | `2` | Napoleon exchanges cards with the kitty | | Play | `3` | Trick play (follow suit; trump is declared suit) | | TrickEnd | `4` | Trick resolved; advance with `next` | | RoundEnd | `5` | All 13 tricks played; advance with `nextround` | | GameEnd | `6` | A player reached the point limit | | Command | Aliases | Description | |----------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bid` | `b` | Place a bid (requires `bid` field) or pass (bid = 0) | | `trump` | `t` | Declare trump suit (requires `suit` field: 1-4) | | `exchange` | `e` | Exchange cards with kitty (requires `cardIndices`) | | `play` | `p` | Play a card (requires `cardIndex`) | | `next` | `n` | Advance to the next trick | | `nextround` | `nr` | Score the round and start the next | | `hint` | `h` | Get a play suggestion | | `log` | `l` | View action log | operationId: napoleonExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NapoleonRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bid: summary: Bid 12 picture cards value: command: bid bid: 12 sessionId: my-session-001 pass: summary: Pass on bidding value: command: bid bid: 0 sessionId: my-session-001 trump: summary: Declare spades as trump value: command: trump suit: 1 sessionId: my-session-001 exchange: summary: Exchange cards at indices 0 and 3 with kitty value: command: exchange cardIndices: [0, 3] sessionId: my-session-001 play: summary: Play card at index 2 value: command: play cardIndex: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/NapoleonResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/NapoleonResponse' /indianpoker/exec: post: tags: - indianpoker summary: Execute an Indian Poker game command description: | Send a command to advance the Indian Poker (インディアンポーカー) game session identified by `sessionId`. Indian Poker is a 4-player betting card game where each player can see everyone else's card but not their own. Each round, one card is dealt to each player and held on their forehead (visible to others). Players bet based on the strength they infer from opponents' cards and their own betting behavior. The game proceeds through these phases: | Phase | Value | Description | |-----------|-------|-------------| | Init | `0` | Initial state before first reset | | Ante | `1` | Ante posted; cards dealt | | Betting | `2` | Betting round (fold/check/call/bet/raise/all-in) | | Showdown | `3` | Cards revealed; winner determined | | End | `4` | A player is eliminated (0 chips) | | Command | Aliases | Description | |-----------|---------|-------------| | `reset` | `r` | Start / restart a game | | `fold` | `f` | Fold the current hand | | `check` | `ck` | Check (no bet, when no outstanding bet) | | `call` | `c` | Call the current bet | | `bet` | `b` | Place a bet (requires `amount`) | | `raise` | `ra` | Raise (requires `amount`) | | `allin` | `a` | Go all-in | | `log` | `l` | View action log | operationId: indianPokerExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/IndianPokerRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 fold: summary: Fold the current hand value: command: fold sessionId: my-session-001 bet: summary: Place a bet of 50 value: command: bet amount: 50 sessionId: my-session-001 call: summary: Call the current bet value: command: call sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/IndianPokerResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/IndianPokerResponse' /videopoker/exec: post: tags: - videopoker summary: Execute a Video Poker game command description: | Send a command to advance the Video Poker (Jacks or Better) game session identified by `sessionId`. Video Poker is a single-player casino card game using a standard 52-card deck. The player bets 1-5 coins, receives 5 cards, chooses which cards to hold, then draws replacements. The final hand is evaluated and paid according to the Jacks or Better pay table. The game proceeds through these phases: | Phase | Value | Description | |---------|-------|-------------| | Bet | `1` | Place a bet (1-5 coins) | | Draw | `2` | Choose cards to hold, then draw replacements | | Result | `3` | Final hand evaluated; payout displayed | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place a bet (requires `amount`: 1-5) | | `hold` | `h` | Hold cards and draw (requires `indices`: array of card positions 0-4) | | `log` | `l` | View action log | operationId: videoPokerExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VideoPokerRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bet: summary: Bet 3 coins value: command: bet amount: 3 sessionId: my-session-001 hold: summary: Hold cards at positions 0, 2, and 4 value: command: hold indices: [0, 2, 4] sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/VideoPokerResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/VideoPokerResponse' /deuceswild/exec: post: tags: - deuceswild summary: Execute a Deuces Wild game command description: | Send a command to advance the Deuces Wild game session identified by `sessionId`. Deuces Wild is a Video Poker variant using a standard 52-card deck where all 2s are wild cards. The player bets 1-5 coins, receives 5 cards, chooses which cards to hold, then draws replacements. The final hand is evaluated using wild card substitution and paid according to the Deuces Wild pay table. The game proceeds through these phases: | Phase | Value | Description | |---------|-------|-------------| | Bet | `1` | Place a bet (1-5 coins) | | Draw | `2` | Choose cards to hold, then draw replacements | | Result | `3` | Final hand evaluated; payout displayed | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place a bet (requires `amount`: 1-5) | | `hold` | `h` | Hold cards and draw (requires `indices`: array of card positions 0-4) | | `log` | `l` | View action log | operationId: deucesWildExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VideoPokerRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bet: summary: Bet 3 coins value: command: bet amount: 3 sessionId: my-session-001 hold: summary: Hold cards at positions 0, 2, and 4 value: command: hold indices: [0, 2, 4] sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: allOf: - $ref: '#/components/schemas/VideoPokerResponse' - type: object properties: variantName: type: string description: Name of the Video Poker variant example: deuceswild '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/VideoPokerResponse' /jokerpoker/exec: post: tags: - jokerpoker summary: Execute a Joker Poker game command description: | Send a command to advance the Joker Poker game session identified by `sessionId`. Joker Poker is a Video Poker variant using a 53-card deck (standard 52 + 1 joker). The joker acts as a wild card. The player bets 1-5 coins, receives 5 cards, chooses which cards to hold, then draws replacements. The final hand is evaluated using wild card substitution and paid according to the Joker Poker pay table. The game proceeds through these phases: | Phase | Value | Description | |---------|-------|-------------| | Bet | `1` | Place a bet (1-5 coins) | | Draw | `2` | Choose cards to hold, then draw replacements | | Result | `3` | Final hand evaluated; payout displayed | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place a bet (requires `amount`: 1-5) | | `hold` | `h` | Hold cards and draw (requires `indices`: array of card positions 0-4) | | `log` | `l` | View action log | operationId: jokerPokerExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VideoPokerRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bet: summary: Bet 3 coins value: command: bet amount: 3 sessionId: my-session-001 hold: summary: Hold cards at positions 0, 2, and 4 value: command: hold indices: [0, 2, 4] sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: allOf: - $ref: '#/components/schemas/VideoPokerResponse' - type: object properties: variantName: type: string description: Name of the Video Poker variant example: jokerpoker '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/VideoPokerResponse' /euchre/exec: post: tags: - euchre summary: Execute a Euchre game command description: | Send a command to advance the Euchre (ユーカー) game session identified by `sessionId`. Euchre is a 4-player, 2-team trick-taking game with a 24-card deck (9-A). Features Right Bower (Jack of trump) and Left Bower (Jack of same-color suit) as highest trump cards. Trump suit determined via bidding. "Going alone" option for bonus points. First team to 10 points wins. The game proceeds through these phases: | Phase | Value | Description | |-----------|-------|-------------| | PickUp | `0` | Decide whether to order up the turned-up card | | CallTrump | `1` | Call a trump suit (after all pass in PickUp) | | Discard | `2` | Dealer discards a card after picking up | | Play | `3` | Trick play (follow suit; bower hierarchy) | | TrickEnd | `4` | Trick resolved; advance with `next` | | RoundEnd | `5` | All 5 tricks played; advance with `nextround` | | GameEnd | `6` | A team reached 10 points | | Command | Aliases | Description | |-------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `orderup` | `o` | Order up the turned-up card | | `orderupalone` | `oa` | Order up and go alone | | `pass` | `pa` | Pass on ordering up / calling trump | | `calltrump` | `c` | Call a trump suit (requires `suit`: 1-4) | | `calltrumpalone` | `ca` | Call a trump suit and go alone | | `discard` | `d` | Discard a card (requires `cardIndex`) | | `play` | `p` | Play a card (requires `cardIndex`) | | `next` | `n` | Advance to the next trick | | `nextround` | `nr` | Score the round and start the next | | `hint` | `h` | Get a play/bid hint | | `log` | `l` | View action log | operationId: euchreExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EuchreRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 orderup: summary: Order up the turned-up card value: command: orderup sessionId: my-session-001 pass: summary: Pass on ordering up value: command: pass sessionId: my-session-001 calltrump: summary: Call hearts as trump value: command: calltrump suit: 3 sessionId: my-session-001 play: summary: Play card at index 2 value: command: play cardIndex: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/EuchreResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/EuchreResponse' /pyramid/exec: post: tags: - pyramid summary: Execute a Pyramid game command description: | Send a command to advance the Pyramid (ピラミッド) game session identified by `sessionId`. Pyramid is a single-player solitaire card game using a standard 52-card deck. 28 cards are dealt face-up in a 7-row pyramid. Remove exposed pairs of cards that sum to 13; Kings (value 13) are removed alone. Remaining 24 cards form the stock pile. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All pyramid cards removed; player wins | | GameOver | `2` | No more moves available; game over | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `draw` | `d` | Draw a card from stock to waste | | `remove` | `rm` | Remove card(s) (pair summing to 13, king, or waste pair) | | `giveup` | `g` | Give up the current game | | `hint` | `h` | Get a hint for the next move | | `undo` | `u` | Undo the last action | | `undo_n` | | Undo N actions (requires `n` parameter) | | `log` | `l` | View action log | operationId: pyramidExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PyramidRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 draw: summary: Draw from stock value: command: draw sessionId: my-session-001 removePair: summary: Remove a pair from pyramid value: command: remove card1: zone: pyramid row: 6 col: 0 card2: zone: pyramid row: 6 col: 1 sessionId: my-session-001 removeKing: summary: Remove a King from pyramid value: command: remove card1: zone: pyramid row: 6 col: 3 sessionId: my-session-001 removeWithWaste: summary: Remove waste top + pyramid card value: command: remove card1: zone: waste card2: zone: pyramid row: 5 col: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/PyramidResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/PyramidResponse' examples: paramError: value: pyramid: [] stockCount: 0 waste: [] phase: 0 moveCount: 0 message: param error. /tripeaks/exec: post: tags: - tripeaks summary: Execute a TriPeaks game command description: | Send a command to advance the TriPeaks (トリピークス) game session identified by `sessionId`. TriPeaks is a single-player solitaire card game using a standard 52-card deck. Three overlapping pyramids (peaks) form a 28-card tableau. Remove exposed cards that are ±1 rank from the waste pile top card (K-A wrap). Win by removing all 28 tableau cards. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All tableau cards removed; player wins | | GameOver | `2` | No more moves available; game over | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `draw` | `d` | Draw a card from stock to waste | | `remove` | `rm` | Remove an exposed card ±1 rank from waste top | | `giveup` | `g` | Give up the current game | | `hint` | `h` | Get a hint for the next move | | `undo` | `u` | Undo the last action | | `undo_n` | | Undo N actions (requires `n` parameter) | | `log` | `l` | View action log | operationId: tripeaksExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TriPeaksRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 draw: summary: Draw from stock value: command: draw sessionId: my-session-001 remove: summary: Remove a tableau card value: command: remove row: 3 col: 0 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/TriPeaksResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/TriPeaksResponse' examples: paramError: value: tableau: [] stockCount: 0 waste: [] phase: 0 moveCount: 0 message: param error. /cribbage/exec: post: tags: - cribbage summary: Execute a Cribbage game command description: | Send a command to advance the Cribbage (クリベッジ) game session identified by `sessionId`. Cribbage is a classic 2-player pegging and scoring card game using a standard 52-card deck. Each player is dealt 6 cards and discards 2 to the crib. A starter card is cut. Players alternate playing cards in the pegging phase (target 31), then score hands and crib. First to 121 points wins. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Discard | `0` | Each player discards 2 cards to the crib | | Cut | `1` | Starter card revealed | | Pegging | `2` | Alternate playing cards toward 31 | | Show | `3` | Score hands and crib | | RoundEnd | `4` | Round summary | | GameEnd | `5` | Game over; winner determined | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `discard` | `d` | Discard cards to the crib (indices) | | `peg` | `p` | Play a card during pegging (index) | | `go` | | Declare "Go" when unable to play | | `shownext` | `sn` | Advance to next show step | | `nextround` | `nr` | Start the next round | | `setdifficulty` | `sd` | Set CPU difficulty (0=Easy, 1=Normal, 2=Hard) | | `setlimit` | `sl` | Set point limit | | `log` | `l` | View action log | operationId: cribbageExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CribbageRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 discard: summary: Discard two cards to crib value: command: discard cardIndices: [4, 5] sessionId: my-session-001 peg: summary: Play a card during pegging value: command: peg cardIndex: 0 sessionId: my-session-001 go: summary: Declare Go value: command: go sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/CribbageResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/CribbageResponse' examples: paramError: value: players: [] phase: 0 roundNumber: 0 currentPlayerIdx: 0 dealerIdx: 0 crib: [] pegCount: 0 pegPlayedCards: [] showPhaseStep: 0 handScoreDetails: [null, null, null] gameEndFlag: false winnerIdx: -1 message: param error. /threecard/exec: post: tags: - threecard summary: Execute a Three Card Poker game command description: | Send a command to advance the Three Card Poker (スリーカードポーカー) game session identified by `sessionId`. Three Card Poker is a single-player casino card game using a standard 52-card deck. Player places an Ante bet and optional Pair Plus side bet, receives 3 cards, then decides to Play (match Ante) or Fold (forfeit Ante). Dealer must have Q-high or better to qualify. Hand rankings (highest to lowest): Straight Flush, Three of a Kind, Straight, Flush, Pair, High Card. The game proceeds through these phases: | Phase | Value | Description | |--------|-------|-------------| | Bet | `1` | Place Ante and optional Pair Plus bet | | Action | `2` | Decide to Play or Fold | | End | `3` | Round complete; shows result and payout | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bet` | `b` | Place Ante bet (requires `amount`; optional `pairPlusBet`) | | `play` | `p` | Place Play bet (equal to Ante) and see dealer's hand | | `fold` | `f` | Forfeit Ante and Pair Plus bets | | `log` | `l` | View action log | operationId: threecardExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ThreeCardRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bet: summary: Bet 100 Ante with 50 Pair Plus value: command: bet amount: 100 pairPlusBet: 50 sessionId: my-session-001 play: summary: Place Play bet value: command: play sessionId: my-session-001 fold: summary: Fold hand value: command: fold sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/ThreeCardResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/ThreeCardResponse' examples: paramError: value: playerHand: [] dealerHand: [] phase: 0 chips: 0 anteBet: 0 pairPlusBet: 0 playBet: 0 result: 0 antePayout: 0 playPayout: 0 anteBonusPayout: 0 pairPlusPayout: 0 totalPayout: 0 dealerQualified: false playerHandRank: 0 dealerHandRank: 0 message: param error. /ohhell/exec: post: tags: - ohhell summary: Execute an Oh Hell game command description: | Send a command to advance the Oh Hell (オー・ヘル) game session identified by `sessionId`. Oh Hell is a 4-player trick-taking game with variable hand sizes per round. Trump suit is determined by the top card of the remaining deck. Players bid the exact number of tricks they expect to win; the dealer is restricted so total bids cannot equal the number of tricks available ("hook" rule). Scoring: exact bid = 10 + bid; miss = 0 (standard) or -|difference| (penalty variant). The game ends after all rounds are played. The game proceeds through these phases: | Phase | Value | Description | |-----------|-------|-------------| | Bid | `0` | Each player declares expected trick wins | | Play | `1` | Trick play (follow suit; trump suit wins) | | TrickEnd | `2` | Trick resolved; advance with `next` | | RoundEnd | `3` | All tricks played; advance with `nextround` | | GameEnd | `4` | All rounds completed | | Command | Aliases | Description | |-------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bid` | `b` | Place a bid (requires `bid` field, 0-handSize) | | `play` | `p` | Play a card (requires `cardIndex`) | | `next` | `n` | Advance to the next trick | | `nextround` | `nr` | Score the round and start the next | | `hint` | `h` | Get recommended bid or play | | `log` | `l` | View action log | operationId: ohhellExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OhHellRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bid: summary: Bid 2 tricks value: command: bid bid: 2 sessionId: my-session-001 play: summary: Play card at index 2 value: command: play cardIndex: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/OhHellResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/OhHellResponse' /bridge/exec: post: tags: - bridge summary: Execute a Contract Bridge game command description: | Send a command to advance the Contract Bridge (コントラクトブリッジ) game session identified by `sessionId`. Contract Bridge is a 4-player, 2-team trick-taking game with a standard 52-card deck (13 cards per player). Features an auction (bidding) phase with Pass/Double/Redouble, a dummy hand (partner of declarer), and rubber bridge scoring. First team to win 2 games wins the rubber. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Bid | `0` | Auction phase (players bid to set the contract) | | Play | `1` | Trick play (follow suit; trump suit wins) | | TrickEnd | `2` | Trick resolved; advance with `next` | | RoundEnd | `3` | All 13 tricks played; advance with `nextround` | | GameEnd | `4` | A team won the rubber (2 games) | | Command | Aliases | Description | |-------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `bid` | `b` | Place a bid (requires `bidType`, and `bidLevel`+`bidSuit` for normal bids) | | `play` | `p` | Play a card (requires `cardIndex`) | | `next` | `n` | Advance to the next trick | | `nextround` | `nr` | Score the round and start the next | | `hint` | `h` | Get recommended bid or play | | `log` | `l` | View action log | operationId: bridgeExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BridgeRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 bidPass: summary: Pass during auction value: command: bid bidType: 0 sessionId: my-session-001 bidNormal: summary: Bid 1 NoTrump value: command: bid bidType: 1 bidLevel: 1 bidSuit: 5 sessionId: my-session-001 play: summary: Play card at index 2 value: command: play cardIndex: 2 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/BridgeResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/BridgeResponse' /pineapple/exec: post: tags: - pineapple summary: Execute a Pineapple Poker game command description: | Send a command to advance the Pineapple Poker (パイナップルポーカー) game session identified by `sessionId`. Pineapple Poker is a Crazy Pineapple variant of Texas Hold'em where each player receives 3 hole cards and must discard 1 after the flop betting round. Supports configurable table sizes: 4-max (default), 6-max, or 9-max. The game proceeds through nine phases: | Phase | Value | Description | |------------|-------|-------------| | Init | `0` | Before any cards are dealt | | PreFlop | `1` | Three hole cards dealt; first betting round | | Discard | `8` | Each player discards 1 hole card (after flop is dealt) | | Flop | `2` | Three community cards revealed; betting round | | Turn | `3` | Fourth community card revealed; betting round | | River | `4` | Fifth community card revealed; betting round | | Showdown | `5` | Hands evaluated; winners determined | | End | `6` | Round complete; results displayed | | Rebuy | `7` | Rebuy/addon decision phase | | Command | Aliases | Description | |-------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `fold` | `f` | Fold and forfeit the pot | | `check` | `ck` | Pass without betting (only when no outstanding bet) | | `call` | `c` | Match the current bet | | `bet` | `b` | Place a bet (use `amount`) | | `raise` | `ra` | Raise by a specified amount on top of calling (use `amount`) | | `allin` | `a` | Go all-in with all remaining chips | | `discard` | `d` | Discard a hole card (use `cardIdx` during Discard phase) | | `rebuy` | `rb` | Buy back chips during rebuy period | | `skiprebuy` | `sr` | Skip rebuy opportunity | | `addon` | `ad` | Add chips (one-time addon purchase) | | `skipaddon` | `sa` | Skip addon opportunity | | `quit` | `q` | End the session | operationId: pineappleExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PineappleRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 resetCustomBlinds: summary: Start with custom blinds value: command: reset smallBlind: 10 bigBlind: 20 sessionId: my-session-001 fold: summary: Fold value: command: fold sessionId: my-session-001 call: summary: Call value: command: call sessionId: my-session-001 bet: summary: Place a bet of 40 value: command: bet amount: 40 sessionId: my-session-001 discard: summary: Discard hole card at index 1 value: command: discard cardIdx: 1 sessionId: my-session-001 allin: summary: Go all-in value: command: allin sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/PineappleResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/PineappleResponse' /speed/exec: post: tags: - speed summary: Execute a Speed (スピード) game command description: | Send a command to advance the Speed game session identified by `sessionId`. Speed is a 2-player (1 human vs 1 CPU) real-time card game. Players play cards ±1 rank onto two shared center piles. When both players are stuck, they flip new cards onto the center piles. First player to empty all cards wins. The game proceeds through three phases: | Phase | Value | Description | |----------|-------|-------------| | Play | `0` | Players play cards onto center piles | | Stuck | `1` | Both players are stuck; flip new center cards | | GameEnd | `2` | A player has emptied all cards | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `play` | `p` | Play a card onto a center pile (requires `cardIndex` and `pileIndex`) | | `flip` | `f` | Flip new cards onto center piles (only in Stuck phase) | | `hint` | `h` | Get a hint for the next move | | `log` | `l` | View action log | operationId: speedExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SpeedRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 play: summary: Play card at hand index 2 onto center pile 0 value: command: play cardIndex: 2 pileIndex: 0 sessionId: my-session-001 flip: summary: Flip new center cards when stuck value: command: flip sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/SpeedResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/SpeedResponse' /gofish/exec: post: tags: - gofish summary: Execute a Go Fish (ゴーフィッシュ) game command description: | Send a command to advance the Go Fish game session identified by `sessionId`. Go Fish is a 4-player (1 human vs 3 CPU) card game. Players ask opponents for a specific rank; if the opponent holds it they surrender all matching cards, otherwise the asker draws from the stock pile ("Go Fish"). Collect all 4 cards of one rank to form a "book". Most books wins. The game proceeds through two phases: | Phase | Value | Description | |----------|-------|-------------| | Play | `0` | Players take turns asking for ranks | | GameEnd | `1` | All 13 books formed or stock exhausted | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `ask` | `a` | Ask an opponent for a rank (requires `target` and `rank`) | | `sd` | — | Set CPU difficulty (requires `amount`: 0=Easy/1=Normal/2=Hard) | | `log` | `l` | View action log | operationId: gofishExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/GoFishRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 ask: summary: Ask player 1 for rank 7 value: command: ask target: 1 rank: 7 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/GoFishResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/GoFishResponse' /golf/exec: post: tags: - golf summary: Execute a Golf Solitaire game command description: | Send a command to advance the Golf Solitaire (ゴルフ) game session identified by `sessionId`. Golf is a single-player solitaire card game using a standard 52-card deck. Seven columns of five face-up cards (35 total) form the tableau. Remove exposed cards (bottom-most per column) that are ±1 rank from the waste pile top card (K-A wrap). Win by removing all 35 tableau cards. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All tableau cards removed; player wins | | GameOver | `2` | No more moves available; game over | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `draw` | `d` | Draw a card from stock to waste | | `remove` | `rm` | Remove an exposed card ±1 rank from waste top | | `giveup` | `g` | Give up the current game | | `hint` | `h` | Get a hint for the next move | | `undo` | `u` | Undo the last action | | `undo_n` | | Undo N actions (requires `n` parameter) | | `log` | `l` | View action log | operationId: golfExec requestBody: required: true content: application/json: schema: type: object required: - command - sessionId properties: command: type: string enum: [reset, r, draw, d, remove, rm, giveup, g, hint, h, undo, u, undo_n, log, l] col: type: integer description: Column index (0-6) for remove command n: type: integer description: Number of undos to perform (required when command is undo_n) example: 3 sessionId: type: string examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 draw: summary: Draw from stock value: command: draw sessionId: my-session-001 remove: summary: Remove a tableau card value: command: remove col: 3 sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: type: object properties: layout: type: array items: type: array items: type: object properties: card: $ref: '#/components/schemas/Card' removed: type: boolean exposed: type: boolean stockCount: type: integer waste: type: array items: $ref: '#/components/schemas/Card' phase: type: integer moveCount: type: integer canUndo: type: boolean isStalemate: type: boolean undoToEscape: type: integer description: Number of undos needed to reach a non-stalemate state (0 if not stalemate, -1 if no escape) example: 0 message: type: string messageCode: type: string messageParams: type: object hint: type: object properties: type: type: string col: type: integer '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: type: object properties: layout: type: array items: type: array stockCount: type: integer waste: type: array items: $ref: '#/components/schemas/Card' phase: type: integer moveCount: type: integer message: type: string /pigtail/exec: post: tags: - pigtail summary: Execute a Pig's Tail game command description: | Send a command to advance the Pig's Tail (ぶたのしっぽ) game session identified by `sessionId`. Pig's Tail is a matching card game for 4 players (1 human + 3 CPU) using a standard 52-card deck. Cards are placed face-down in a circle pile. Players take turns drawing 1 card and placing it face-up on the center pile. If the drawn card's suit matches the center top card's suit, the player takes all center cards as a penalty. When the circle pile is empty, the player with the most cards loses. The game proceeds through these phases: | Phase | Value | Description | |----------|-------|-------------| | Play | `0` | Game in progress | | GameEnd | `1` | Circle pile empty; game over | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `draw` | `d` | Draw a card from the circle pile | | `log` | `l` | View action log | operationId: pigtailExec requestBody: required: true content: application/json: schema: type: object required: - command - sessionId properties: command: type: string enum: [reset, r, draw, d, log, l] sessionId: type: string examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 draw: summary: Draw a card from circle pile value: command: draw sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: type: object properties: players: type: array items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' circleCount: type: integer description: Remaining cards in the circle pile centerTop: nullable: true allOf: - $ref: '#/components/schemas/Card' description: Top card of the center pile (null if empty) centerCount: type: integer description: Number of cards in the center pile currentTurn: type: integer gameEndFlag: type: boolean loserIdx: type: integer description: Index of the losing player (-1 if game not ended) lastDrawCard: nullable: true allOf: - $ref: '#/components/schemas/Card' lastPenalty: type: boolean description: Whether the last draw triggered a suit-match penalty cpuActions: type: array items: type: object properties: drawPlayerIdx: type: integer drawnCard: nullable: true allOf: - $ref: '#/components/schemas/Card' penaltyFlag: type: boolean penaltyCount: type: integer hesitationMs: type: integer humanAction: nullable: true type: object properties: drawPlayerIdx: type: integer drawnCard: nullable: true allOf: - $ref: '#/components/schemas/Card' penaltyFlag: type: boolean penaltyCount: type: integer message: type: string messageCode: type: string messageParams: type: object '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: type: object properties: players: type: array items: type: object circleCount: type: integer centerCount: type: integer gameEndFlag: type: boolean loserIdx: type: integer message: type: string /sevencardstud/exec: post: tags: - sevencardstud summary: Execute a Seven Card Stud game command description: | Send a command to advance the Seven Card Stud game session identified by `sessionId`. Seven Card Stud is a classic stud poker variant with no community cards. Each player receives 2 face-down hole cards and 1 face-up door card on Third Street, then 1 face-up card each on Fourth through Sixth Street, and 1 final face-down card on Seventh Street. Uses antes and bring-in (lowest door card) instead of blinds. Betting order changes each street based on best visible hand (door cards). Best 5 out of 7 cards wins. The game supports configurable table sizes: 2-max through 7-max (default 4, i.e., 1 human + 3 CPU). The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Init | `0` | Before any cards are dealt | | ThirdSt | `1` | 2 hole cards + 1 door card; first betting round (bring-in) | | FourthSt | `2` | 4th card (face-up); betting round | | FifthSt | `3` | 5th card (face-up); betting round | | SixthSt | `4` | 6th card (face-up); betting round | | SeventhSt | `5` | 7th card (face-down); final betting round | | Showdown | `6` | Hands evaluated; winners determined | | End | `7` | Round complete; results displayed | | Rebuy | `8` | Rebuy/addon decision phase | | Command | Aliases | Description | |---------|---------|-------------| | `reset` | `r` | Start / restart a game | | `fold` | `f` | Fold and forfeit the pot | | `check` | `ck` | Pass without betting (only when no outstanding bet) | | `call` | `c` | Match the current bet | | `bet` | `b` | Place a bet (use `amount`) | | `raise` | `ra` | Raise by a specified amount on top of calling (use `amount`) | | `allin` | `a` | Go all-in with all remaining chips | | `rebuy` | `rb` | Buy back chips during rebuy period | | `skiprebuy` | `sr` | Skip rebuy opportunity | | `addon` | `ad` | Add chips (one-time addon purchase) | | `skipaddon` | `sa` | Skip addon opportunity | | `muck` | `m` | Muck (hide) hand at showdown | | `show` | `sh` | Show hand at showdown | | `log` | `l` | View action log | | `quit` | `q` | End the session | operationId: sevencardstudExec requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SevenCardStudRequest' examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 resetCustomAnte: summary: Start with custom ante and bring-in value: command: reset ante: 5 bringIn: 10 smallBet: 20 bigBet: 40 sessionId: my-session-001 fold: summary: Fold value: command: fold sessionId: my-session-001 call: summary: Call value: command: call sessionId: my-session-001 bet: summary: Place a bet of 40 value: command: bet amount: 40 sessionId: my-session-001 raise: summary: Raise by 80 value: command: raise amount: 80 sessionId: my-session-001 allin: summary: Go all-in value: command: allin sessionId: my-session-001 rebuy: summary: Accept rebuy value: command: rebuy sessionId: my-session-001 skipaddon: summary: Skip addon value: command: skipaddon sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: $ref: '#/components/schemas/SevenCardStudResponse' '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: $ref: '#/components/schemas/SevenCardStudResponse' examples: paramError: value: players: [] pot: 0 sidePots: [] dealerIdx: 0 currentTurn: 0 phase: 0 gameEndFlag: false lastBet: 0 minRaise: 0 roundResults: [] cpuActions: [] message: param error. /clocksolitaire/exec: post: tags: - clocksolitaire summary: Execute a Clock Solitaire game command description: | Send a command to advance the Clock Solitaire (クロックソリティア) game session identified by `sessionId`. Clock Solitaire is a fully automatic single-player solitaire card game using a standard 52-card deck. 52 cards are dealt face-down into 13 piles of 4 cards: 12 piles placed at clock positions 1-12 and 1 pile in the center (King position). One card at a time is flipped face-up and placed at the bottom of the pile matching its rank. The game clears when all cards are face-up before the 4th King is placed; the game is over when the 4th King is turned while face-down cards remain. The game proceeds through these phases: | Phase | Value | Description | |------------|-------|-------------| | Playing | `0` | Game in progress | | GameClear | `1` | All cards are face-up; player wins | | GameOver | `2` | 4th King turned while face-down cards remain | | Command | Aliases | Description | |------------|---------|-------------| | `reset` | `r` | Start / restart a game | | `step` | `s` | Flip and place one card | | `autoplay` | `a` | Play all remaining cards to completion | | `log` | `l` | View action log | operationId: clocksolitaireExec requestBody: required: true content: application/json: schema: type: object required: - command - sessionId properties: command: type: string enum: [reset, r, step, s, autoplay, a, log, l] sessionId: type: string examples: reset: summary: Start a new game value: command: reset sessionId: my-session-001 step: summary: Flip and place one card value: command: step sessionId: my-session-001 autoplay: summary: Play all remaining cards to completion value: command: autoplay sessionId: my-session-001 responses: '200': description: Command processed successfully content: application/json: schema: type: object properties: piles: type: array description: | 13 piles indexed 0-12 (index 0 = position 1, index 11 = position 12, index 12 = center/King). Each pile is an array of card objects. items: type: array items: type: object properties: card: $ref: '#/components/schemas/Card' faceUp: type: boolean faceUpCount: type: integer description: Total number of face-up cards across all piles phase: type: integer description: "0=Playing, 1=GameClear, 2=GameOver" stepCount: type: integer description: Number of cards flipped so far currentCard: description: The card most recently flipped (null before first step) nullable: true allOf: - $ref: '#/components/schemas/Card' message: type: string messageCode: type: string messageParams: type: object '400': description: Bad request (unsupported command, missing/invalid parameters, or session error) content: application/json: schema: type: object properties: piles: type: array items: type: array faceUpCount: type: integer phase: type: integer stepCount: type: integer message: type: string components: schemas: # ------------------------------------------------------------------------- # Shared types # ------------------------------------------------------------------------- Card: type: object description: A single playing card required: - design - value properties: design: type: string description: | Card suit. `JOKER` is only present in games that use a Joker (Old Maid, Daifugo, Sevens). enum: [SPADE, CLOVER, HEART, DIAMOND, JOKER] example: HEART value: type: integer description: | Card rank. 1 = Ace, 2–10 = pip, 11 = Jack, 12 = Queen, 13 = King. 0 indicates the Joker. minimum: 0 maximum: 13 example: 7 # ------------------------------------------------------------------------- # BlackJack # ------------------------------------------------------------------------- BlackJackRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bet` (`b`), `hit` (`h`), `stand` (`s`), `doubledown` (`d`), `split` (`sp`), `insurance` (`i`), `declineinsurance` (`di`), `togglesoft17` (`soft17`), `togglecounting` (`counting`), `toggledas` (`das`), `setcountingsystem` (`scs`), `setpenetration` (`pen`), `earlysurrender` (`es`), `declineearlysurrender` (`des`), `setsurrenderrule` (`ssr`), `quit` (`q`). enum: [r, reset, b, bet, h, hit, s, stand, d, doubledown, sp, split, i, insurance, di, declineinsurance, sur, surrender, togglehint, sd, setdeckcount, togglesoft17, soft17, togglecounting, counting, toggledas, das, scs, setcountingsystem, pen, setpenetration, es, earlysurrender, des, declineearlysurrender, ssr, setsurrenderrule, log, q, quit] example: hit amount: type: integer description: | Bet amount. Required for `bet` command (minimum 10). Deck count for `setdeckcount` command (valid values: 1, 2, 4, 6, 8). Counting system for `setcountingsystem` command (0=Hi-Lo, 1=KO, 2=Zen Count, 3=Omega II). Penetration rate for `setpenetration` command (valid values: 50, 75). Surrender rule for `setsurrenderrule` command (0=Late, 1=Early, 2=No surrender). Ignored for other commands. minimum: 1 example: 100 dealerHitsSoft17: type: boolean description: | Whether the dealer hits on soft 17. Only used with `reset` command. When true, dealer hits on soft 17 (H17); when false, dealer stands (S17). Defaults to false (S17) when omitted. example: false cpuPlayerCount: type: integer description: | Number of CPU players at the table (0–3). Only used with `reset` command. CPU players use basic strategy. Defaults to 0 when omitted. minimum: 0 maximum: 3 example: 0 countingEnabled: type: boolean description: | Whether card counting display is enabled. Only used with `reset` command. Defaults to false when omitted. example: false doubleAfterSplit: type: boolean description: | Whether double-after-split (DAS) is allowed. Only used with `reset` command. Defaults to true when omitted. example: true countingSystem: type: integer description: | Card counting system (0=Hi-Lo, 1=KO, 2=Zen Count, 3=Omega II). Only used with `reset` command. Defaults to 0 (Hi-Lo) when omitted. minimum: 0 maximum: 3 example: 0 deckPenetration: type: integer description: | Deck penetration rate in percent (50 or 75). Only used with `reset` command. Defaults to 75 when omitted. At 75%, the shoe is reshuffled when less than 25% of cards remain. At 50%, the shoe is reshuffled when less than 50% of cards remain. enum: [50, 75] example: 75 perfectPairsBet: type: integer description: | Perfect Pairs side bet amount. Only used with `bet` command. Must be 0 (no bet) or a positive multiple of 10. Defaults to 0 when omitted. minimum: 0 example: 0 twentyOnePlus3Bet: type: integer description: | 21+3 side bet amount. Only used with `bet` command. Must be 0 (no bet) or a positive multiple of 10. Defaults to 0 when omitted. minimum: 0 example: 0 handCount: type: integer description: | Number of initial hands to deal (1–3). Only used with `bet` command. Each hand receives the same bet amount. Side bets apply only to hand 0. Defaults to 1 when omitted. 0 is treated as 1. minimum: 0 maximum: 3 example: 1 surrenderRule: type: integer description: | Surrender rule (0=Late surrender, 1=Early surrender, 2=No surrender). Only used with `reset` command. Defaults to 0 (Late surrender) when omitted. minimum: 0 maximum: 2 example: 0 sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 BlackJackPlayer: type: object required: - chips properties: score: type: integer description: | Hand score. Present only for the **dealer**. `0` while the game is in progress; the actual score once the game ends. For the **player**, score is available via the `hands` array. example: 18 cards: type: array description: | Cards currently held. Present only for the **dealer**. Only the face-up card (index 0) is returned while the game is in progress; all cards are returned once the game ends. For the **player**, cards are available via the `hands` array. items: $ref: '#/components/schemas/Card' chips: type: integer description: Current chip count for this player. example: 1000 BlackJackHand: type: object required: - score - cards - bet - stood - doubled - busted - isBlackJack - canSplit - surrendered - canSurrender properties: score: type: integer description: Hand score (Ace counted optimally as 1 or 11). example: 18 cards: type: array description: Cards in this hand. items: $ref: '#/components/schemas/Card' bet: type: integer description: Bet amount for this hand. example: 100 stood: type: boolean description: Whether the player has stood on this hand. doubled: type: boolean description: Whether the player doubled down on this hand. busted: type: boolean description: Whether this hand has busted (score > 21). isBlackJack: type: boolean description: Whether this hand is a natural blackjack (2 cards, score 21). canSplit: type: boolean description: Whether this hand can be split (2 cards of equal BJ value). surrendered: type: boolean description: Whether the player surrendered this hand. canSurrender: type: boolean description: Whether the player can surrender this hand (exactly 2 cards, action phase, not yet stood/busted/surrendered). BlackJackResponse: type: object required: - dealer - player - phase - currentHandIdx - insuranceBet - insuranceAvailable - message - hintEnabled - suggestedAction - deckCount - dealerHitsSoft17 - countingEnabled - cpuPlayerCount - runningCount - trueCount - perfectPairsBet - twentyOnePlus3Bet - doubleAfterSplit properties: dealer: $ref: '#/components/schemas/BlackJackPlayer' player: $ref: '#/components/schemas/BlackJackPlayer' hands: type: array description: | Player hands. Contains 1–3 initial hands (based on `handCount` in bet); may grow further after splits (up to 4 per initial hand, 8 total). Not present in bet phase. items: $ref: '#/components/schemas/BlackJackHand' phase: type: integer description: | Current game phase: - `1` — Bet phase - `2` — Deal phase - `3` — Insurance phase - `4` — Action phase - `5` — End phase - `6` — Early Surrender phase (only when surrender rule = Early) enum: [1, 2, 3, 4, 5, 6] example: 4 currentHandIdx: type: integer description: | Index of the currently active hand (0-based). Relevant when player has split into multiple hands. example: 0 insuranceBet: type: integer description: Amount of insurance bet placed (0 if none). example: 0 insuranceAvailable: type: boolean description: Whether insurance is available (dealer shows Ace). message: type: string description: | Game result message. One of: - `""` — game in progress - `"It is a draw."` — tie - `"You are the winner."` — player wins - `"It is your loss."` — player loses - `"bye."` — session ended by quit command - `"param error."` — invalid request - `"error."` — internal error example: You are the winner. messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hintEnabled: type: boolean description: Whether the basic strategy hint is currently enabled. example: false suggestedAction: type: integer description: | Basic strategy suggested action for the current hand (0 when hint is disabled or no suggestion). - `0` — None - `1` — Hit - `2` — Stand - `3` — Double Down (else Hit) - `4` — Split - `5` — Surrender - `6` — Decline Insurance - `7` — Double Down (else Stand) enum: [0, 1, 2, 3, 4, 5, 6, 7] example: 1 deckCount: type: integer description: | Number of decks in the shoe. Valid values: 1, 2, 4, 6, 8. enum: [1, 2, 4, 6, 8] example: 1 dealerHitsSoft17: type: boolean description: Whether the dealer hits on soft 17 (H17). False means dealer stands on soft 17 (S17). example: false countingEnabled: type: boolean description: Whether card counting display is currently enabled. example: false cpuPlayerCount: type: integer description: Number of CPU players at the table (0–3). minimum: 0 maximum: 3 example: 0 doubleAfterSplit: type: boolean description: Whether double-after-split (DAS) is currently allowed. example: true countingSystem: type: integer description: | Current card counting system (0=Hi-Lo, 1=KO, 2=Zen Count, 3=Omega II). minimum: 0 maximum: 3 example: 0 deckPenetration: type: integer description: | Current deck penetration rate in percent (50 or 75). Controls when the shoe is reshuffled based on remaining cards. enum: [50, 75] example: 75 runningCount: type: integer description: | Running count based on the active counting system. Only meaningful when `countingEnabled` is true. Positive values favor the player; negative values favor the dealer. example: 0 trueCount: type: number description: | True count (running count divided by estimated remaining decks). Only meaningful when `countingEnabled` is true. Returns 0 for unbalanced systems (KO). example: 0.0 cpuPlayers: type: array description: | CPU player seats. Empty array when `cpuPlayerCount` is 0. Each entry contains the CPU player's chip count and hands. items: $ref: '#/components/schemas/BlackJackCpuSeat' perfectPairsBet: type: integer description: Current Perfect Pairs side bet amount (0 if no side bet placed). example: 0 twentyOnePlus3Bet: type: integer description: Current 21+3 side bet amount (0 if no side bet placed). example: 0 sideBetResults: type: array description: | Side bet evaluation results. Present after dealing when side bets were placed. Each entry describes one side bet outcome (win or loss). items: $ref: '#/components/schemas/BlackJackSideBetResult' multiHandCount: type: integer description: | Number of initial hands dealt this round (1–3). 0 means the default single hand. Only meaningful after a bet has been placed. minimum: 0 maximum: 3 example: 1 surrenderRule: type: integer description: | Current surrender rule: - `0` — Late surrender (default; surrender allowed after dealer BJ check) - `1` — Early surrender (surrender allowed before dealer BJ check) - `2` — No surrender (surrender disabled) minimum: 0 maximum: 2 example: 0 BlackJackSideBetResult: type: object description: Result of a single side bet evaluation required: - betType - resultType - resultName - betAmount - payout properties: betType: type: integer description: | Side bet type: - `1` — Perfect Pairs - `2` — 21+3 enum: [1, 2] example: 1 resultType: type: integer description: | Result type. For Perfect Pairs: - `0` — None (no pair) - `1` — Perfect Pair (same suit, same value; 25:1) - `2` — Colored Pair (same color, same value; 12:1) - `3` — Mixed Pair (different color, same value; 6:1) For 21+3: - `0` — None (no hand) - `1` — Suited Trips (same suit, same value; 100:1) - `2` — Straight Flush (same suit, consecutive; 40:1) - `3` — Three of a Kind (same value, different suits; 30:1) - `4` — Straight (consecutive values; 10:1) - `5` — Flush (same suit; 5:1) example: 1 resultName: type: string description: Human-readable name of the result (e.g., "Perfect Pair", "Straight Flush"). example: Perfect Pair betAmount: type: integer description: Amount wagered on this side bet. example: 10 payout: type: integer description: Payout amount (0 if lost; betAmount × multiplier if won). example: 250 BlackJackCpuSeat: type: object description: A CPU player seat in a multi-player BlackJack game required: - chips - hands - insuranceBet properties: chips: type: integer description: Current chip count for this CPU player. example: 1000 hands: type: array description: Hands for this CPU player (one hand normally; multiple after split). items: $ref: '#/components/schemas/BlackJackHand' insuranceBet: type: integer description: | Amount of insurance bet placed by this CPU player (0 if none). CPUs take insurance when card counting is enabled and the count >= 3. example: 0 # ------------------------------------------------------------------------- # Poker # ------------------------------------------------------------------------- PokerRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `exchange` (`e`), `stand` (`s`), `bet` (`b`), `call` (`c`), `raise` (`ra`), `check` (`ck`), `fold` (`f`), `allin` (`a`), `odds` (`o`), `quit` (`q`). enum: [r, reset, e, exchange, s, stand, b, bet, c, call, ra, raise, ck, check, f, fold, a, allin, o, odds, log, q, quit] example: bet indices: type: array description: | Zero-based indices of cards to exchange (for the `exchange` command) or to calculate draw odds for (for the `odds` command). Omit or send an empty array to keep all cards. items: type: integer minimum: 0 maximum: 4 example: [0, 2] amount: type: integer description: | Bet/raise amount (for the `bet` and `raise` commands). Minimum bet is 10. minimum: 10 example: 20 cpuCount: type: integer description: Number of CPU players (used with `reset` command) minimum: 1 maximum: 3 example: 2 jokerCount: type: integer description: Number of joker cards (used with `reset` command) minimum: 0 maximum: 2 example: 1 bettingLimit: type: integer description: | Betting limit type (used with `reset` command). 0 = Fixed (default), 1 = Pot Limit, 2 = No Limit. minimum: 0 maximum: 2 example: 0 isLowball: type: boolean description: | Enable 2-7 Lowball mode (used with `reset` command). When true, the lowest hand wins. Ace always counts as 14, straights and flushes count against the player, and jokers are forced to rank 0. Best hand is 2-3-4-5-7 off-suit. example: false cpuMetaAI: type: boolean description: | When true, CPUs adaptively adjust bluff and call behavior based on session-scoped game history (Meta-AI). Defaults to false. default: false example: false humanPlayMs: type: integer description: Human play hesitation time in milliseconds (0=not measured) example: 1200 sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 PokerPlayerData: type: object required: - id - isHuman - cards - chips - currentBet - folded - allIn - handRank - handName - exchangeCount - playStyleName properties: id: type: integer description: Player index (0 = human) isHuman: type: boolean cards: type: array items: $ref: '#/components/schemas/Card' description: Player's cards. CPU cards hidden (empty) until End phase. chips: type: integer minimum: 0 currentBet: type: integer minimum: 0 folded: type: boolean allIn: type: boolean handRank: type: integer description: "0=HighCard..9=RoyalFlush, 10=FiveOfAKind (joker only)" minimum: 0 maximum: 10 handName: type: string exchangeCount: type: integer minimum: -1 playStyleName: type: string description: "CPU play style name (Conservative/Balanced/Aggressive/Bluffer)" PokerCpuAction: type: object properties: playerIdx: type: integer action: type: integer description: "0=Fold,1=Check,2=Call,3=Bet,4=Raise,5=AllIn" amount: type: integer PokerCpuExchange: type: object properties: playerIdx: type: integer exchangeCount: type: integer PokerResult: type: object properties: playerIdx: type: integer handRank: type: integer handName: type: string kickers: type: string description: Kicker card values formatted as display string (e.g., "A, Q, 10"). Empty for hands without kickers. wonAmount: type: integer PokerSidePot: type: object properties: amount: type: integer eligiblePlayers: type: array items: type: integer PokerOdds: type: object description: Draw odds for a single hand rank required: - handRank - handName - probability - count - total properties: handRank: type: integer description: "Hand rank index (0=HighCard..10=FiveOfAKind)" minimum: 0 maximum: 10 handName: type: string description: Human-readable hand name (e.g. "Flush", "Full House") probability: type: number format: double description: Probability of achieving this hand (0.0 to 1.0) minimum: 0 maximum: 1 count: type: integer description: Number of combinations that result in this hand minimum: 0 total: type: integer description: Total number of combinations evaluated minimum: 0 PokerResponse: type: object required: - players - pot - sidePots - dealerIdx - currentTurn - phase - gameEndFlag - lastBet - minRaise - ante - jokerCount - roundResults - cpuActions - cpuExchanges - message properties: players: type: array items: $ref: '#/components/schemas/PokerPlayerData' pot: type: integer minimum: 0 sidePots: type: array items: $ref: '#/components/schemas/PokerSidePot' dealerIdx: type: integer currentTurn: type: integer phase: type: integer description: | Current game phase: - `0` — Init (no cards dealt yet) - `1` — Deal (first betting round) - `2` — Exchange (player can exchange cards) - `3` — SecondBet (second betting round) - `4` — End (showdown; all hands visible) enum: [0, 1, 2, 3, 4] gameEndFlag: type: boolean lastBet: type: integer minRaise: type: integer ante: type: integer jokerCount: type: integer bettingLimit: type: integer description: | Betting limit type: 0 = Fixed, 1 = Pot Limit, 2 = No Limit. enum: [0, 1, 2] raiseCount: type: integer description: Number of raises in the current betting round. minimum: 0 maxBetAmount: type: integer description: | Maximum allowed bet/raise amount for Pot Limit mode. 0 means no limit (Fixed or No Limit mode). minimum: 0 isLowball: type: boolean description: Whether 2-7 Lowball mode is active. roundResults: type: array items: $ref: '#/components/schemas/PokerResult' cpuActions: type: array items: $ref: '#/components/schemas/PokerCpuAction' cpuExchanges: type: array items: $ref: '#/components/schemas/PokerCpuExchange' odds: type: array description: | Draw odds for each hand rank. Only present when the `odds` command is used during the exchange phase. items: $ref: '#/components/schemas/PokerOdds' message: type: string messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Old Maid # ------------------------------------------------------------------------- OldMaidRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `draw` (`d`), `shuffle` (`s`), `reorder`, `quit` (`q`). enum: [r, reset, d, draw, s, shuffle, reorder, log, q, quit] example: draw drawIdx: type: integer description: | Zero-based index of the card to draw from the target player's hand. Omit (or send `null`) for random selection. nullable: true minimum: 0 example: 1 reorderIndices: type: array description: | Permutation of indices for reordering the human player's hand. Required when command is `reorder`. Must be a valid permutation of `[0, 1, ..., handSize-1]`. nullable: true items: type: integer minimum: 0 example: [2, 0, 1] mode: type: integer description: Game mode (0=Normal/ババ抜き, 1=JijiNuki/ジジ抜き) default: 0 example: 0 cpuPlacementStrategy: type: boolean description: When true, CPUs place the odd card at the edge of their hand default: false example: false cpuMemoryAI: type: boolean description: When true, CPUs remember draw positions and adjust selection strategy default: false example: false cpuHesitationEnabled: type: boolean description: When true, CPU actions include a hesitation delay (ms) that varies based on draw outcome default: false example: false cpuMetaAI: type: boolean description: When true, CPUs adaptively adjust their strategy based on session-scoped game history (Meta-AI) default: false example: false sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 OldMaidPlayer: type: object required: - id - isHuman - isFinished - cardCount - cards properties: id: type: integer description: Player index (0 = human, 1–3 = CPU) example: 0 isHuman: type: boolean description: Whether this is the human player example: true isFinished: type: boolean description: Whether this player has finished (all cards matched and discarded) example: false cardCount: type: integer description: Number of cards remaining in hand example: 13 cards: type: array description: Cards in hand. CPU players' cards are always an empty array. items: $ref: '#/components/schemas/Card' OldMaidCpuAction: type: object description: Record of a CPU player's draw action during their turn required: - drawPlayerIdx - drawFromIdx - drawnCard - discardedPairs properties: drawPlayerIdx: type: integer description: Index of the CPU player who performed the draw example: 1 drawFromIdx: type: integer description: Index of the player the card was drawn from example: 0 drawnCard: $ref: '#/components/schemas/Card' discardedPairs: type: integer description: Number of pairs discarded as a result of this draw example: 1 hesitationMs: type: integer description: CPU hesitation delay in milliseconds (0 when feature is disabled) example: 750 OldMaidDrawHistoryEntry: type: object description: A single draw event in the game-wide draw history (card info omitted for fairness) required: - drawPlayerIdx - drawFromIdx - discardedPairs - drawerFinished - targetFinished properties: drawPlayerIdx: type: integer description: Index of the player who drew a card example: 0 drawFromIdx: type: integer description: Index of the player the card was drawn from example: 1 discardedPairs: type: integer description: Number of pairs discarded as a result of this draw example: 0 drawerFinished: type: boolean description: Whether the drawing player finished (0 cards) after this draw example: false targetFinished: type: boolean description: Whether the target player finished (0 cards) after this draw example: true OldMaidResponse: type: object required: - players - currentTurn - nextDrawTargetIdx - gameEndFlag - loserIdx - lastDrawPlayerIdx - lastDrawFromIdx - lastDrawCard - lastDiscardedPairs - hasDrawn - cpuActions - message - cpuHighlightedCardIdx - mode properties: players: type: array description: State of all 4 players items: $ref: '#/components/schemas/OldMaidPlayer' currentTurn: type: integer description: Index of the player whose turn it is example: 0 nextDrawTargetIdx: type: integer description: Index of the player the human must draw from on their next turn example: 1 gameEndFlag: type: boolean description: Whether the game has ended example: false loserIdx: type: integer description: Index of the losing player (-1 while game is in progress) example: -1 lastDrawPlayerIdx: type: integer description: Index of the player who last drew a card example: 0 lastDrawFromIdx: type: integer description: Index of the player the last card was drawn from example: 1 lastDrawCard: allOf: - $ref: '#/components/schemas/Card' nullable: true description: The card drawn most recently (null before any draw) lastDiscardedPairs: type: integer description: Number of pairs discarded after the last draw example: 0 hasDrawn: type: boolean description: Whether the human player has drawn this turn example: false cpuActions: type: array description: List of CPU actions that occurred since the last human action items: $ref: '#/components/schemas/OldMaidCpuAction' drawHistory: type: array description: Full draw history throughout the entire game (card info omitted for fairness) items: $ref: '#/components/schemas/OldMaidDrawHistoryEntry' message: type: string description: | Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. cpuHighlightedCardIdx: type: integer description: Index of the highlighted card for the target CPU player (-1 if none) example: -1 removedCard: allOf: - $ref: '#/components/schemas/Card' nullable: true description: The removed card (only revealed at game end in JijiNuki mode) mode: type: integer description: Current game mode (0=Normal/ババ抜き, 1=JijiNuki/ジジ抜き) example: 0 metaAI: type: object nullable: true description: Meta-AI state for the current session (null when Meta-AI is disabled) properties: enabled: type: boolean description: Whether Meta-AI is enabled example: true gamesPlayed: type: integer description: Number of games played in the current session example: 3 edgePickRate: type: number format: float description: Rate at which the human player picks edge cards (0.0–1.0) example: 0.45 # ------------------------------------------------------------------------- # Daifugo # ------------------------------------------------------------------------- DaifugoRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `play` (`p`), `sort`, `quit` (`q`). enum: [r, reset, p, play, sort, log, q, quit] example: play indices: type: array description: | Zero-based indices of cards to play (for the `play` command). Send an empty array to pass. items: type: integer minimum: 0 maxItems: 13 example: [0, 1] sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 config: allOf: - $ref: '#/components/schemas/DaifugoConfig' nullable: true description: | Optional rule configuration for the `reset` command. When present, overrides the current session's rule settings. Omit (or set to null) to reuse the previous configuration. sortMode: type: integer nullable: true description: | Sort mode for the `sort` command. 0 = by strength (default), 1 = by suit, 2 = by number. enum: [0, 1, 2] example: 0 DaifugoPlayer: type: object required: - id - isHuman - isFinished - rank - cardCount - cards properties: id: type: integer description: Player index (0 = human, 1–3 = CPU) example: 0 isHuman: type: boolean description: Whether this is the human player example: true isFinished: type: boolean description: Whether this player has played all their cards example: false rank: type: integer description: | Final rank once the game ends (-1 while game is in progress). 1 = 大富豪, 2 = 富豪, 3 = 平民, 4 = 大貧民. example: -1 cardCount: type: integer description: Number of cards remaining in hand example: 8 cards: type: array description: Cards in hand (only populated for the human player) items: $ref: '#/components/schemas/Card' illegalFinishPenalty: type: boolean description: Whether this player received an illegal finish penalty (反則上がり) example: false DaifugoAction: type: object description: Record of a player's action (play or pass) required: - playerIdx - playedCards properties: playerIdx: type: integer description: Index of the player who acted example: 1 playedCards: type: array description: Cards played. Null indicates a pass. nullable: true items: $ref: '#/components/schemas/Card' DaifugoConfig: type: object properties: jokerCount: type: integer description: Number of jokers in the deck example: 2 eightCutEnabled: type: boolean description: Whether 8切り (eight-cut) rule is enabled example: true suitLockMode: type: integer description: "Suit lock mode: 0=none, 1=partial, 2=full" enum: [0, 1, 2] example: 1 elevenBackEnabled: type: boolean description: Whether 11バック (eleven-back) rule is enabled example: true sequenceEnabled: type: boolean description: Whether 階段 (sequence/straight) rule is enabled example: true cardExchangeEnabled: type: boolean description: Whether カード交換 (card exchange) based on previous ranks is enabled example: true blindExchangeEnabled: type: boolean description: Whether ブラインドカード交換 (blind card exchange — upper-rank player gives random cards instead of weakest) rule is enabled (default false) example: false fiveSkipEnabled: type: boolean description: Whether 5飛び (five-skip) rule is enabled (default false) example: false fiveSkipCount: type: integer description: Number of players to skip per 5 card minimum: 1 maximum: 5 example: 1 sevenPassEnabled: type: boolean description: Whether 7渡し (seven-pass) rule is enabled (default false) example: false tenDiscardEnabled: type: boolean description: Whether 10捨て (ten-discard) rule is enabled (default false) example: false spadeThreeEnabled: type: boolean description: Whether スペ3返し (spade-three counter) rule is enabled (default false) example: false capitalFallEnabled: type: boolean description: Whether 都落ち (capital-fall) rule is enabled (default false) example: false nineReverseEnabled: type: boolean description: Whether 9リバース (nine-reverse) rule is enabled (default false) example: false coupDetatEnabled: type: boolean description: Whether クーデター (coup d'état — 3 nines = revolution) rule is enabled (default false) example: false numberLockEnabled: type: boolean description: Whether 激シバ (number lock) rule is enabled (default false) example: false queenBomberEnabled: type: boolean description: Whether Qボンバー (queen bomber) rule is enabled (default false) example: false sandstormEnabled: type: boolean description: Whether 砂嵐 (sandstorm — 3 non-joker 3s clear the table like 8-cut) rule is enabled (default false) example: false emperorEnabled: type: boolean description: Whether エンペラー (emperor — 4 consecutive cards of all different suits on clear table triggers revolution + table clear) rule is enabled (default false) example: false sequenceRevolutionEnabled: type: boolean description: Whether 階段革命 (sequence revolution — 4+ card sequences trigger revolution) rule is enabled (default false) example: false sequenceLockEnabled: type: boolean description: Whether 階段縛り (sequence lock — playing a sequence on a sequence locks to sequences; persists across table clears) rule is enabled (default false) example: false illegalFinishEnabled: type: boolean description: Whether 反則上がり (illegal finish — finishing with 8-cut, joker, or revolution is penalized) rule is enabled (default false) example: false cpuDifficulty: type: integer description: | CPU difficulty level. 0 = Normal (default, balanced AI with card preservation), 1 = Easy (simple greedy play), 2 = Hard (strategic AI that adapts to opponent hand sizes) enum: [0, 1, 2] example: 0 DaifugoExchangeAction: type: object properties: fromPlayerIdx: type: integer description: Index of the player who gave cards example: 3 toPlayerIdx: type: integer description: Index of the player who received cards example: 0 cards: type: array description: Cards that were exchanged items: $ref: '#/components/schemas/Card' DaifugoResponse: type: object required: - players - currentTurn - tableCards - lastPlayPlayerIdx - gameEndFlag - revolutionActive - elevenBackActive - suitLocked - lockedSuit - tableIsSequence - config - exchangeActions - cpuActions - humanAction - message - pendingAction - pendingActionTarget - reverseDirection - numberLocked - sequenceLocked - sortMode properties: players: type: array description: State of all 4 players items: $ref: '#/components/schemas/DaifugoPlayer' currentTurn: type: integer description: Index of the player whose turn it is example: 0 tableCards: type: array description: Cards currently on the table (last played set) items: $ref: '#/components/schemas/Card' lastPlayPlayerIdx: type: integer description: Index of the player who last played cards example: 0 gameEndFlag: type: boolean description: Whether the game has ended example: false revolutionActive: type: boolean description: Whether revolution is active (strength order reversed) example: false elevenBackActive: type: boolean description: Whether 11-back is active (temporary strength reversal) example: false suitLocked: type: boolean description: Whether suit lock is active example: false lockedSuit: type: string description: The locked suit name (e.g. SPADE, HEART) when suitLocked is true example: "" tableIsSequence: type: boolean description: Whether the current table play is a sequence (階段) example: false config: $ref: '#/components/schemas/DaifugoConfig' exchangeActions: type: array description: Card exchange actions that occurred at game start (based on previous rankings) items: $ref: '#/components/schemas/DaifugoExchangeAction' cpuActions: type: array description: CPU actions that occurred after the human's last action items: $ref: '#/components/schemas/DaifugoAction' humanAction: allOf: - $ref: '#/components/schemas/DaifugoAction' nullable: true description: The human player's most recent action (null before first play) message: type: string description: | Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. pendingAction: type: string description: | Pending action that the current player must resolve before turn advances. `none` = no pending action; `sevenPass` = must give a card (7渡し); `tenDiscard` = must discard a card (10捨て); `queenBomber` = must discard a card from target player (Qボンバー). enum: [none, sevenPass, tenDiscard, queenBomber] example: none pendingActionTarget: type: integer description: | For `sevenPass`, the index of the player to give the card to. -1 when no pending action. example: -1 reverseDirection: type: boolean description: Whether turn direction is reversed (9リバース) example: false numberLocked: type: boolean description: Whether number lock (連番縛り / 激シバ) is active example: false sequenceLocked: type: boolean description: Whether sequence lock (階段縛り) is active — only sequences or emperor can be played when table is clear example: false sortMode: type: integer description: | Current sort mode for the human player's hand. 0 = by strength, 1 = by suit, 2 = by number. example: 0 # ------------------------------------------------------------------------- # Sevens # ------------------------------------------------------------------------- SevensRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `play` (`p`), `joker` (`j`), `quit` (`q`). enum: [r, reset, p, play, j, joker, log, q, quit] example: play index: type: integer description: | Zero-based index of the card to play (for the `play` and `joker` commands). Use `-1` to pass (play command only). default: -1 example: 3 jokerTargetSuit: type: integer description: | Target suit for joker placement (for the `joker` command). 1 = SPADE, 2 = CLOVER, 3 = HEART, 4 = DIAMOND. default: 0 example: 1 jokerTargetValue: type: integer description: | Target value for joker placement (for the `joker` command). Range: 1–13. default: 0 example: 6 tunnelEnabled: type: boolean description: | Enable tunnel rule (A↔K circular). Only used with `reset` command. example: false tunnelSkipWidth: type: integer description: | Tunnel skip width (0 = disabled, 2–12 = add ±N skip connections). When tunnelEnabled is true, skip connections wrap around (A↔K circular). Only used with `reset` command. minimum: 0 maximum: 12 default: 0 example: 0 jokerCount: type: integer description: | Number of jokers in the deck (0–2). Only used with `reset` command. minimum: 0 maximum: 2 example: 0 cpuStrategy: type: integer description: | CPU strategy mode (0 = simple, 1 = strategic, 2 = harassment). Only used with `reset` command. enum: [0, 1, 2] example: 0 maxPasses: type: integer description: | Maximum number of passes per player (0 = unlimited). Only used with `reset` command. minimum: 0 default: 5 example: 5 noJokerFinish: type: boolean description: | Ban finishing the game by playing a joker as the last card. Only used with `reset` command. example: false jokerReclaim: type: boolean description: | Enable joker reclaim rule (playing a real card on a joker-occupied position returns the joker to the player's hand). Only used with `reset` command. example: false endStop: type: boolean description: | Enable end stop rule (片側ストップ). When A is placed, the high side (8–K) is blocked; when K is placed, the low side (A–6) is blocked. Only used with `reset` command. example: false jokerConsecutiveBanned: type: boolean description: | Enable joker consecutive banned rule (ジョーカー連続禁止). When enabled, a player who played a joker on their last turn cannot play a joker on the current turn. Playing a normal card or passing resets the restriction. Only used with `reset` command. example: false sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 SevensPlayer: type: object required: - id - isHuman - isFinished - rank - cardCount - passesUsed - maxPasses - cards properties: id: type: integer description: Player index (0 = human, 1–3 = CPU) example: 0 isHuman: type: boolean description: Whether this is the human player example: true isFinished: type: boolean description: Whether this player has played all their cards example: false rank: type: integer description: | Final rank once the player finishes (-1 while still playing). example: -1 cardCount: type: integer description: Number of cards remaining in hand example: 5 passesUsed: type: integer description: Number of passes the player has used example: 0 maxPasses: type: integer description: Maximum number of passes allowed example: 3 cards: type: array description: Cards in hand (only populated for the human player) items: $ref: '#/components/schemas/Card' lastPlayedJoker: type: boolean description: Whether this player played a joker on their last turn (only meaningful for the human player when jokerConsecutiveBanned is enabled) example: false SevensAction: type: object description: Record of a player's action (play or pass) required: - playerIdx - playedCard - targetSuit - targetValue properties: playerIdx: type: integer description: Index of the player who acted example: 1 playedCard: allOf: - $ref: '#/components/schemas/Card' nullable: true description: The card played. Null indicates a pass. targetSuit: type: integer description: | Target suit for joker placement (0 for non-joker plays). example: 0 targetValue: type: integer description: | Target value for joker placement (0 for non-joker plays). example: 0 forcedPass: type: boolean description: | Whether the pass was forced (player had no playable cards). Only meaningful when `playedCard` is null. example: false SevensConfig: type: object description: Game configuration for optional rules required: - tunnelEnabled - tunnelSkipWidth - jokerCount - cpuStrategy - maxPasses - noJokerFinish properties: tunnelEnabled: type: boolean description: Whether the tunnel rule (A↔K circular) is enabled example: false tunnelSkipWidth: type: integer description: Tunnel skip width (0 = disabled, 2–12 = ±N skip connections added) example: 0 jokerCount: type: integer description: Number of joker cards in the deck example: 0 cpuStrategy: type: integer description: CPU strategy mode (0 = simple, 1 = strategic, 2 = harassment) enum: [0, 1, 2] example: 0 maxPasses: type: integer description: Maximum number of passes per player (0 = unlimited) example: 5 noJokerFinish: type: boolean description: Whether finishing the game with a joker as the last card is banned example: false jokerReclaimEnabled: type: boolean description: Whether ジョーカー回収 (joker reclaim — playing a real card on a joker-occupied position returns the joker to the player's hand) is enabled (default false) example: false endStopEnabled: type: boolean description: Whether 片側ストップ (end stop — A placed blocks high side 8–K, K placed blocks low side A–6) is enabled (default false) example: false jokerConsecutiveBanned: type: boolean description: Whether ジョーカー連続禁止 (joker consecutive banned — a player who played a joker last turn cannot play a joker this turn) is enabled (default false) example: false SevensResponse: type: object required: - players - currentTurn - tableMinVals - tableMaxVals - tablePlaced - config - gameEndFlag - cpuActions - humanAction - message properties: players: type: array description: State of all 4 players items: $ref: '#/components/schemas/SevensPlayer' currentTurn: type: integer description: Index of the player whose turn it is example: 0 tableMinVals: type: array description: | Minimum card value currently placed on the board per suit. Array of 5 integers indexed by suit (0 = unused/no suit, 1 = SPADE, 2 = CLOVER, 3 = HEART, 4 = DIAMOND). Derived from `tablePlaced` bitmask for backward compatibility. items: type: integer minItems: 5 maxItems: 5 example: [0, 6, 7, 7, 7] tableMaxVals: type: array description: | Maximum card value currently placed on the board per suit. Uses the same indexing as `tableMinVals`. Derived from `tablePlaced` bitmask for backward compatibility. items: type: integer minItems: 5 maxItems: 5 example: [0, 8, 7, 7, 7] tablePlaced: type: array description: | Bitmask per suit indicating which card values are placed on the board. Array of 5 integers indexed by suit. Bit i set means value i is placed. Example: 128 (binary 10000000) = only value 7 is placed. items: type: integer minItems: 5 maxItems: 5 example: [0, 192, 128, 128, 128] config: $ref: '#/components/schemas/SevensConfig' gameEndFlag: type: boolean description: Whether the game has ended example: false cpuActions: type: array description: CPU actions that occurred after the human's last action items: $ref: '#/components/schemas/SevensAction' humanAction: allOf: - $ref: '#/components/schemas/SevensAction' nullable: true description: The human player's most recent action (null before first play) message: type: string description: | Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # Doubt DoubtRequest: type: object required: - command - sessionId properties: command: type: string description: Game command enum: [reset, r, play, p, doubt, d, skip, s, log, quit, q] example: play cardIndices: type: array description: Hand indices of cards to play (required for `play` command) items: type: integer example: [0, 2] claimedValue: type: integer description: Declared card value 1–13 (required for `play` command) minimum: 1 maximum: 13 example: 3 doubterIndices: type: array description: | Player indices declaring doubt. Used only for the `doubt` command: include 0 (human) if the human is doubting, plus any CPU doubters. Ignored for the `skip` command (the server determines CPU doubters from its own state). items: type: integer example: [0, 2] doubtWindowSec: type: integer description: | Duration of the doubt window in seconds (used with the `reset` command). Must be at least 1. Defaults to 10 when omitted. minimum: 1 example: 5 cpuMemoryLevel: type: integer description: | CPU memory retention level (used with the `reset` command). 0 = Easy (~30% retention), 1 = Normal (~70% retention), 2 = Hard (100% retention). Defaults to 1 (Normal) when omitted. minimum: 0 maximum: 2 example: 1 penaltyDrawLimit: type: integer description: | Maximum number of cards the loser picks up after a doubt resolution (used with the `reset` command). 0 = unlimited (loser takes all table cards). Remaining cards are discarded from the game. Defaults to 0 when omitted. minimum: 0 example: 5 cpuHesitationEnabled: type: boolean description: | When true, CPU actions include a hesitation delay (ms) that varies based on whether the CPU is bluffing. Bluff → 60% fast (300-500ms), 40% slow (1200-1800ms); honest → medium (600-1000ms). Defaults to false. default: false example: false cpuMetaAI: type: boolean description: | When true, CPUs adaptively adjust bluff and doubt behavior based on session-scoped game history (Meta-AI). Defaults to false. default: false example: false humanPlayMs: type: integer description: Human play hesitation time in milliseconds (0=not measured) example: 1200 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 DoubtPlayer: type: object description: A player in the Doubt game required: - id - isHuman - isFinished - cardCount - cards properties: id: type: integer description: Player index (0 = human, 1–3 = CPU) example: 0 isHuman: type: boolean description: Whether this player is the human example: true isFinished: type: boolean description: Whether this player has played all cards (won) example: false cardCount: type: integer description: Number of cards in hand example: 5 cards: type: array description: Cards in hand (populated only for the human player) items: $ref: '#/components/schemas/Card' DoubtAction: type: object description: An action (play) taken by a player required: - playerIdx - claimedValue - cardCount - isBluff properties: playerIdx: type: integer description: Index of the player who took the action example: 1 claimedValue: type: integer description: The card value the player declared example: 3 cardCount: type: integer description: Number of cards played example: 2 isBluff: type: boolean description: Whether the play was a bluff (always false until revealed) example: false hasTell: type: boolean description: Whether the CPU showed a visible "tell" (nervousness indicator) during this play example: false hesitationMs: type: integer description: CPU hesitation delay in milliseconds (0 when feature is disabled) example: 750 DoubtDoubtResult: type: object description: Result of a doubt resolution required: - doubterIdx - cardPlayerIdx - wasLying - loserIdx - cardCount - discardedCount - revealedCards properties: doubterIdx: type: integer description: Index of the player who called doubt example: 0 cardPlayerIdx: type: integer description: Index of the player whose cards were challenged example: 1 wasLying: type: boolean description: Whether the challenged player was bluffing example: true loserIdx: type: integer description: Index of the player who must pick up the table cards example: 1 cardCount: type: integer description: Number of cards the loser picks up example: 4 discardedCount: type: integer description: Number of cards discarded from the game (when penaltyDrawLimit is active and table exceeds the limit) example: 0 revealedCards: type: array description: Cards that were revealed during the doubt resolution items: $ref: '#/components/schemas/Card' DoubtResponse: type: object description: Current state of the Doubt game required: - players - currentTurn - phase - tableCardCount - cpuDoubters - cpuActions - gameEndFlag - winnerIdx - message - doubtWindowSec - penaltyDrawLimit properties: players: type: array description: All players in turn order items: $ref: '#/components/schemas/DoubtPlayer' currentTurn: type: integer description: Index of the player whose turn it is example: 0 phase: type: integer description: "Game phase: 0 = Play, 1 = Doubt window, 2 = Game end" enum: [0, 1, 2] example: 0 tableCardCount: type: integer description: Total number of cards on the table example: 0 lastAction: allOf: - $ref: '#/components/schemas/DoubtAction' nullable: true description: The most recent play action (null until first play) cpuDoubters: type: array description: Indices of CPU players who have decided to call doubt items: type: integer example: [] cpuActions: type: array description: CPU play actions that occurred after the human's last action items: $ref: '#/components/schemas/DoubtAction' humanAction: allOf: - $ref: '#/components/schemas/DoubtAction' nullable: true description: The human player's most recent play action (null before first play) lastDoubtResult: allOf: - $ref: '#/components/schemas/DoubtDoubtResult' nullable: true description: Result of the most recent doubt resolution (null if no doubt yet) gameEndFlag: type: boolean description: Whether the game has ended example: false winnerIdx: type: integer description: Index of the winner (-1 while game is in progress) example: -1 message: type: string description: | Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. doubtWindowSec: type: integer description: Duration of the doubt window in seconds (reflects current game config) example: 10 penaltyDrawLimit: type: integer description: Maximum number of cards the loser picks up after a doubt resolution (0 = unlimited) example: 0 metaAI: type: object nullable: true description: Meta-AI state for the current session (null when Meta-AI is disabled) properties: enabled: type: boolean description: Whether Meta-AI is enabled example: true gamesPlayed: type: integer description: Number of games played in the current session example: 5 bluffRate: type: number format: float description: Rate at which the human player bluffs (0.0–1.0) example: 0.35 doubtAccuracy: type: number format: float description: Accuracy of the human player's doubt calls (0.0–1.0) example: 0.60 hesitationMean: type: number format: float description: Average human hesitation time in milliseconds example: 950.0 # ------------------------------------------------------------------------- # Texas Hold'em # ------------------------------------------------------------------------- HoldemRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `fold` (`f`), `check` (`ck`), `call` (`c`), `bet` (`b`), `raise` (`ra`), `allin` (`a`), `rebuy` (`rb`), `skiprebuy` (`sr`), `addon` (`ad`), `skipaddon` (`sa`), `muck` (`m`), `show` (`sh`), `quit` (`q`). enum: [r, reset, f, fold, ck, check, c, call, b, bet, ra, raise, a, allin, rb, rebuy, sr, skiprebuy, ad, addon, sa, skipaddon, m, muck, sh, show, log, q, quit] example: bet amount: type: integer description: | Bet/raise amount (for the `bet` and `raise` commands). Ignored for other commands. minimum: 1 example: 40 smallBlind: type: integer description: | Small blind amount (for the `reset` command). Defaults to 5 when omitted. If only `smallBlind` is specified and it is >= the default `bigBlind`, `bigBlind` is auto-adjusted to `smallBlind * 2`. minimum: 1 example: 5 bigBlind: type: integer description: | Big blind amount (for the `reset` command). Defaults to 10 when omitted. If only `bigBlind` is specified and the default `smallBlind` is >= `bigBlind`, `smallBlind` is auto-adjusted to `bigBlind / 2` (minimum 1). minimum: 1 example: 10 tournamentMode: type: boolean description: | Enable tournament mode with blind escalation (for the `reset` command). Defaults to false when omitted. example: false blindLevelHands: type: integer description: | Number of hands before blinds increase in tournament mode (for the `reset` command). Defaults to 10 when omitted. Must be >= 1. minimum: 1 example: 10 blindMultiplier: type: integer description: | Blind multiplier as a percentage when blinds increase (for the `reset` command). Defaults to 200 (= 2x). Must be >= 101. minimum: 101 example: 200 bettingLimit: type: integer description: | Betting limit type (used with `reset` command). 0 = Fixed (default), 1 = Pot Limit, 2 = No Limit. minimum: 0 maximum: 2 example: 0 tableSize: type: integer description: | Table size (used with `reset` command). 4 = 4-max (default), 6 = 6-max, 9 = 9-max. enum: [4, 6, 9] example: 4 rebuyEnabled: type: boolean description: | Enable rebuy (used with `reset` command). Defaults to false when omitted. example: false rebuyMaxCount: type: integer description: | Maximum number of rebuys per player (used with `reset` command). Defaults to 3 when omitted. minimum: 1 example: 3 rebuyChips: type: integer description: | Chips granted per rebuy (used with `reset` command). Defaults to 1000 when omitted. minimum: 1 example: 1000 rebuyPeriodHands: type: integer description: | Number of hands during which rebuy is available (used with `reset` command). Defaults to 10 when omitted. minimum: 1 example: 10 addonEnabled: type: boolean description: | Enable addon (used with `reset` command). Defaults to false when omitted. example: false addonChips: type: integer description: | Chips granted by addon (used with `reset` command). Defaults to 1500 when omitted. minimum: 1 example: 1500 addonAfterHand: type: integer description: | Hand number after which addon becomes available (used with `reset` command). Defaults to 10 when omitted. minimum: 1 example: 10 cpuMetaAI: type: boolean description: | When true, CPUs adaptively adjust bluff and call behavior based on session-scoped game history (Meta-AI). Defaults to false. default: false example: false humanPlayMs: type: integer description: Human play hesitation time in milliseconds (0=not measured) example: 1200 sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 HoldemPlayer: type: object required: - id - isHuman - cards - chips - currentBet - folded - allIn - handRank - handName - bestHand - playStyleName properties: id: type: integer description: Player index (0 = human, 1+ = CPU) example: 0 isHuman: type: boolean description: Whether this is the human player example: true cards: type: array description: | Hole cards. Human player's cards are always visible. CPU cards are hidden (empty array) until showdown. items: $ref: '#/components/schemas/Card' chips: type: integer description: Current chip count minimum: 0 example: 990 currentBet: type: integer description: Current round bet amount minimum: 0 example: 10 folded: type: boolean description: Whether the player has folded example: false allIn: type: boolean description: Whether the player is all-in example: false handRank: type: integer description: | Numeric hand rank (only populated at showdown for non-folded players): 0 = High Card, 1 = One Pair, 2 = Two Pair, 3 = Three of a Kind, 4 = Straight, 5 = Flush, 6 = Full House, 7 = Four of a Kind, 8 = Straight Flush, 9 = Royal Flush. minimum: 0 maximum: 9 example: 0 handName: type: string description: Human-readable hand name (empty until showdown) example: "" bestHand: type: array description: Best 5-card hand (only populated at showdown) items: $ref: '#/components/schemas/Card' playStyleName: type: string description: | CPU play style name: TAG (Tight-Aggressive), LAP (Loose-Passive), TAP (Tight-Passive), LAG (Loose-Aggressive). Empty for human player. example: TAG totalHands: type: integer description: Total number of hands played (for HUD stats) minimum: 0 example: 10 vpip: type: integer description: VPIP percentage (Voluntarily Put $ In Pot) — preflop call/bet/raise/allin rate minimum: 0 maximum: 100 example: 50 pfr: type: integer description: PFR percentage (Pre-Flop Raise) — preflop bet/raise/allin rate minimum: 0 maximum: 100 example: 25 threeBet: type: integer description: 3Bet percentage — preflop re-raise rate when facing a raise minimum: 0 maximum: 100 example: 10 af: type: string description: "Aggression Factor (postflop bets+raises / calls). Values: decimal (e.g. \"2.5\"), \"∞\" (bets but no calls), \"-\" (no postflop actions)" example: "2.5" HoldemCpuAction: type: object description: Record of a CPU player's betting action required: - playerIdx - action - amount properties: playerIdx: type: integer description: Index of the CPU player who acted example: 1 action: type: integer description: | Action taken: 0 = Fold, 1 = Check, 2 = Call, 3 = Bet, 4 = Raise, 5 = All-in. enum: [0, 1, 2, 3, 4, 5] example: 2 amount: type: integer description: Amount bet/raised (0 for fold/check/call/all-in) example: 0 HoldemResult: type: object description: Result for a player at showdown required: - playerIdx - handRank - handName - bestHand - wonAmount properties: playerIdx: type: integer description: Index of the player example: 0 handRank: type: integer description: Numeric hand rank example: 1 handName: type: string description: Hand name (e.g., "One Pair") example: One Pair kickers: type: string description: Kicker card values formatted as display string (e.g., "A, Q, 10"). Empty for hands without kickers. bestHand: type: array description: Best 5-card hand items: $ref: '#/components/schemas/Card' wonAmount: type: integer description: Chips won from the pot example: 40 mucked: type: boolean description: Whether the player mucked (hid) their hand at showdown example: false HoldemSidePot: type: object description: A side pot created when a player goes all-in required: - amount - eligiblePlayers properties: amount: type: integer description: Total chips in this side pot example: 100 eligiblePlayers: type: array description: Indices of players eligible to win this pot items: type: integer example: [0, 1, 2] HoldemResponse: type: object required: - players - communityCards - pot - sidePots - dealerIdx - currentTurn - phase - gameEndFlag - lastBet - minRaise - roundResults - cpuActions - message properties: players: type: array description: State of all players (4/6/9 depending on table size) items: $ref: '#/components/schemas/HoldemPlayer' communityCards: type: array description: Community cards on the board (0/3/4/5 cards based on phase) items: $ref: '#/components/schemas/Card' pot: type: integer description: Current main pot size minimum: 0 example: 30 sidePots: type: array description: Side pots (created when players go all-in with different amounts) items: $ref: '#/components/schemas/HoldemSidePot' dealerIdx: type: integer description: Index of the dealer for this round example: 0 currentTurn: type: integer description: Index of the player whose turn it is example: 0 phase: type: integer description: | Current game phase: - `0` — Init - `1` — PreFlop - `2` — Flop - `3` — Turn - `4` — River - `5` — Showdown - `6` — End - `7` — Rebuy/Addon enum: [0, 1, 2, 3, 4, 5, 6, 7] example: 1 gameEndFlag: type: boolean description: Whether the round has ended example: false lastBet: type: integer description: The current highest bet in this betting round minimum: 0 example: 10 minRaise: type: integer description: Minimum raise amount minimum: 0 example: 20 bettingLimit: type: integer description: | Betting limit type: 0 = Fixed, 1 = Pot Limit, 2 = No Limit. enum: [0, 1, 2] raiseCount: type: integer description: Number of raises in the current betting round. minimum: 0 maxBetAmount: type: integer description: | Maximum allowed bet/raise amount for Pot Limit mode. 0 means no limit (Fixed or No Limit mode). minimum: 0 roundResults: type: array description: Results for each non-folded player at showdown items: $ref: '#/components/schemas/HoldemResult' cpuActions: type: array description: CPU actions that occurred since the last human action items: $ref: '#/components/schemas/HoldemCpuAction' message: type: string description: | Game result or status message. One of: - `""` — game in progress - `"You are the winner."` — player wins - `"You lose."` — player loses - `"You folded."` — player folded - `"Game over."` — game ended (no results) - `"Unsupported command."` — unknown command - `"bye."` — session ended by quit command - `"param error."` — invalid request - `"error."` — internal error example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. handCount: type: integer description: Number of hands played (for tournament mode display) minimum: 0 example: 0 smallBlind: type: integer description: Current small blind amount minimum: 1 example: 5 bigBlind: type: integer description: Current big blind amount minimum: 2 example: 10 tournamentMode: type: boolean description: Whether tournament mode (blind escalation) is active example: false blindLevelHands: type: integer description: Number of hands before blinds increase minimum: 1 example: 10 blindMultiplier: type: integer description: Blind multiplier percentage (e.g. 200 = 2x) minimum: 101 example: 200 tableSize: type: integer description: Current table size (4/6/9) enum: [4, 6, 9] example: 4 rebuyPhaseType: type: integer description: | Type of rebuy phase: 1 = rebuy, 2 = addon. Only present when phase is 7 (Rebuy/Addon). enum: [1, 2] rebuyChips: type: integer description: Chips available for current rebuy minimum: 0 rebuyMaxCount: type: integer description: Maximum rebuy count setting minimum: 0 rebuyCounts: type: array description: Rebuy count per player (same order as players array) items: type: integer minimum: 0 addonChips: type: integer description: Chips available for addon minimum: 0 muckAvailable: type: boolean description: Whether the human player can muck (hide) their hand at showdown (available when human lost) example: false equity: $ref: '#/components/schemas/HoldemEquity' potOdds: type: number format: double description: Pot odds percentage (0-100). Only present during PreFlop-River phases. example: 33.33 HoldemEquity: type: object description: Equity (win probability) calculated via Monte Carlo simulation. Only present during PreFlop-River phases when human is active. required: - winProbability - handOdds properties: winProbability: type: number format: double description: Win probability (0.0 - 1.0) minimum: 0 maximum: 1 example: 0.75 handOdds: type: array items: $ref: '#/components/schemas/HoldemHandOdds' HoldemHandOdds: type: object required: - handRank - handName - probability properties: handRank: type: integer description: Hand rank (0=HighCard, 1=OnePair, ..., 9=RoyalFlush) minimum: 0 example: 1 handName: type: string description: Hand rank name example: "One Pair" probability: type: number format: double description: Probability of achieving this hand rank (0.0 - 1.0) minimum: 0 maximum: 1 example: 0.5 # ------------------------------------------------------------------------- # Action Log (common across all games) # ------------------------------------------------------------------------- ActionLogEntry: type: object required: - turnNumber - playerIdx - actionType - detail properties: turnNumber: type: integer description: Sequential turn number starting from 1 minimum: 1 example: 1 playerIdx: type: integer description: Index of the player who performed the action (0 = human, 1+ = CPU) example: 0 actionType: type: string description: Type of action (e.g., "hit", "stand", "bet", "play", "draw", "doubt", "fold") example: hit detail: type: string description: Human-readable description of the action example: "Player 0 hit and drew ♠A" cards: type: array description: Cards involved in the action (optional; revealed even if originally hidden) items: $ref: '#/components/schemas/Card' ActionLogResponse: type: object required: - entries properties: entries: type: array description: | Cumulative action log (棋譜) for the current game session. All hidden cards are revealed in the log entries. items: $ref: '#/components/schemas/ActionLogEntry' # ------------------------------------------------------------------------- # Hearts # ------------------------------------------------------------------------- HeartsRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `pass`, `play` (`p`), `next` (`n`), `nextround` (`nr`), `setdifficulty` (`sd`), `setlimit` (`sl`), `log` (`l`), `quit` (`q`). enum: [reset, r, pass, play, p, next, n, nextround, nr, setdifficulty, sd, setlimit, sl, log, l, quit, q] example: play cardIndices: type: array description: | Hand indices of the 3 cards to pass (required for `pass` command). Must contain exactly 3 indices. items: type: integer example: [0, 3, 7] cardIndex: type: integer description: Hand index of the card to play (required for `play` command) example: 2 config: type: object description: Game configuration options (used with `reset`, `setdifficulty`, `setlimit` commands) properties: difficulty: type: integer description: "CPU difficulty level: 0 = Easy (random), 1 = Normal (point avoidance), 2 = Hard (strategic). Defaults to 1." minimum: 0 maximum: 2 example: 1 pointLimit: type: integer description: "Point limit to end the game. Defaults to 100." minimum: 1 example: 100 omnibusJD: type: boolean description: "Omnibus Hearts variant: J♦ awards -10 points (beneficial). Defaults to false." example: false sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 HeartsPlayer: type: object description: A player in the Hearts game required: - id - isHuman - score - roundScore - cards - cardCount properties: id: type: integer description: Player index (0 = human, 1–3 = CPU) example: 0 isHuman: type: boolean description: Whether this player is the human example: true score: type: integer description: Cumulative score across all rounds example: 15 roundScore: type: integer description: Score accumulated in the current round example: 3 cards: type: array description: Cards in hand (populated only for the human player) items: $ref: '#/components/schemas/Card' cardCount: type: integer description: Number of cards in hand example: 13 HeartsTrickCard: type: object description: A card played in the current trick required: - playerIdx - card properties: playerIdx: type: integer description: Index of the player who played this card example: 1 card: $ref: '#/components/schemas/Card' HeartsResponse: type: object description: Current state of the Hearts game required: - players - phase - roundNumber - trickNumber - currentPlayerIdx - currentTrick - heartsBroken - passDirection - gameEndFlag - winnerIdx - message properties: players: type: array description: All players in seat order items: $ref: '#/components/schemas/HeartsPlayer' phase: type: integer description: "Game phase: 0 = Pass, 1 = Play, 2 = TrickEnd, 3 = RoundEnd, 4 = GameEnd" enum: [0, 1, 2, 3, 4] example: 1 roundNumber: type: integer description: Current round number (1-based) example: 1 trickNumber: type: integer description: Current trick number within the round (1-based) example: 1 currentPlayerIdx: type: integer description: Index of the player whose turn it is example: 0 currentTrick: type: array description: Cards played in the current trick so far items: $ref: '#/components/schemas/HeartsTrickCard' heartsBroken: type: boolean description: Whether hearts have been broken this round example: false passDirection: type: integer description: "Pass direction for the current round: 0 = left, 1 = right, 2 = across, 3 = none" enum: [0, 1, 2, 3] example: 0 gameEndFlag: type: boolean description: Whether the game has ended example: false winnerIdx: type: integer description: Index of the winner (-1 while game is in progress) example: -1 message: type: string description: Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. config: type: object description: Current game configuration properties: difficulty: type: integer description: "CPU difficulty level: 0 = Easy, 1 = Normal, 2 = Hard" example: 1 pointLimit: type: integer description: Point limit to end the game example: 100 omnibusJD: type: boolean description: "Omnibus Hearts variant: J♦ awards -10 points" example: false hint: type: object nullable: true description: Recommended play hint (only present when hint command is used) properties: cardIndices: type: array items: type: integer description: Recommended card indices (1 for play, 3 for pass) reason: type: string description: Machine-readable reason key for the hint # ------------------------------------------------------------------------- # Memory # ------------------------------------------------------------------------- MemoryRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `flip` (`f`), `next` (`n`), `log` (`l`). enum: [reset, r, flip, f, next, n, log, l] example: flip position: type: integer description: Board position of the card to flip (required for `flip` command, 0-based index) example: 5 config: type: object description: Game configuration options (used with `reset` command) properties: cpuDifficulty: type: integer description: "CPU difficulty level: 0 = Easy (random), 1 = Normal (partial memory), 2 = Hard (perfect memory). Defaults to 1." minimum: 0 maximum: 2 example: 1 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 MemoryPlayer: type: object description: A player in the Memory game required: - id - isHuman - pairCount properties: id: type: integer description: Player index (0 = human, 1–3 = CPU) example: 0 isHuman: type: boolean description: Whether this player is the human example: true pairCount: type: integer description: Number of pairs collected example: 3 MemoryBoardCard: type: object description: A card on the Memory board required: - faceUp - taken properties: faceUp: type: boolean description: Whether the card is currently face-up example: false taken: type: boolean description: Whether the card has been taken as part of a pair example: false card: description: Card details (only present when face-up) $ref: '#/components/schemas/Card' MemoryResponse: type: object description: Current state of the Memory game required: - players - phase - board - currentPlayerIdx - firstFlipPos - secondFlipPos - lastMatchResult - turnNumber - gameEndFlag - winnerIdx - message - config properties: players: type: array description: All players in seat order items: $ref: '#/components/schemas/MemoryPlayer' phase: type: integer description: "Game phase: 0 = Flip1, 1 = Flip2, 2 = Result, 3 = GameEnd" enum: [0, 1, 2, 3] example: 0 board: type: array description: All 52 card positions on the board items: $ref: '#/components/schemas/MemoryBoardCard' currentPlayerIdx: type: integer description: Index of the player whose turn it is example: 0 firstFlipPos: type: integer description: Board position of the first flipped card (-1 if none) example: -1 secondFlipPos: type: integer description: Board position of the second flipped card (-1 if none) example: -1 lastMatchResult: type: boolean description: Whether the last flip pair was a match example: false turnNumber: type: integer description: Current turn number (incremented after each pair flip) example: 0 gameEndFlag: type: boolean description: Whether the game has ended example: false winnerIdx: type: integer description: Index of the winner (-1 while game is in progress) example: -1 message: type: string description: Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. config: type: object description: Current game configuration required: - cpuDifficulty properties: cpuDifficulty: type: integer description: "CPU difficulty level: 0 = Easy, 1 = Normal, 2 = Hard" example: 1 # ------------------------------------------------------------------------- # Klondike # ------------------------------------------------------------------------- KlondikeZone: type: object description: Specifies a zone on the Klondike board for move commands required: - zone properties: zone: type: string description: "Zone name: waste, tableau, or foundation" enum: [waste, tableau, foundation] example: tableau col: type: integer description: Column index (0-based, required for tableau and foundation zones) example: 3 cardIndex: type: integer description: Index of the card within the column (for moving multiple cards from tableau) example: 0 KlondikeRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `draw` (`d`), `move` (`m`), `giveup` (`g`), `hint` (`h`), `autocomplete` (`ac`), `log` (`l`). enum: [reset, r, draw, d, move, m, giveup, g, hint, h, autocomplete, ac, undo, u, undo_n, log, l] example: move from: description: Source zone for a move command $ref: '#/components/schemas/KlondikeZone' to: description: Destination zone for a move command $ref: '#/components/schemas/KlondikeZone' config: type: object description: Game configuration (used with reset command) properties: drawCount: type: integer description: "Number of cards to draw from stock (1 or 3, default 1)" enum: [1, 3] example: 3 scoringMode: type: integer description: "Scoring mode: 0 = None, 1 = Vegas" enum: [0, 1] example: 0 n: type: integer description: Number of undos to perform (required when command is undo_n) example: 3 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 KlondikeTableauCard: type: object description: A card in a Klondike tableau column required: - faceUp properties: faceUp: type: boolean description: Whether the card is face-up example: true card: description: Card details (only present when face-up) $ref: '#/components/schemas/Card' KlondikeResponse: type: object description: Current state of the Klondike game required: - tableau - stockCount - waste - foundation - phase - moveCount - drawCount - canUndo - isStalemate - undoToEscape - score - scoringMode - message properties: tableau: type: array description: The 7 tableau columns, each containing an array of cards items: type: array items: $ref: '#/components/schemas/KlondikeTableauCard' stockCount: type: integer description: Number of cards remaining in the stock pile example: 24 waste: type: array description: Cards in the waste pile (top card is the last element) items: $ref: '#/components/schemas/Card' foundation: type: array description: The 4 foundation piles (one per suit), each containing an array of cards items: type: array items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 0 = Playing, 1 = GameClear, 2 = GameOver" enum: [0, 1, 2] example: 0 moveCount: type: integer description: Total number of moves made in the current game example: 0 drawCount: type: integer description: "Number of cards drawn per draw action (1 or 3)" enum: [1, 3] example: 1 canUndo: type: boolean description: Whether the undo action is available example: false isStalemate: type: boolean description: Whether the game is in a stalemate (no moves can lead to a win) example: false undoToEscape: type: integer description: Number of undos needed to reach a non-stalemate state (0 if not stalemate, -1 if no escape) example: 0 score: type: integer description: "Vegas score: -52 + 5 × foundation cards" example: -52 scoringMode: type: integer description: "Scoring mode: 0 = None, 1 = Vegas" enum: [0, 1] example: 0 message: type: string description: Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object description: Hint for the next available move (only present when hint command is used) properties: from: $ref: '#/components/schemas/KlondikeZone' to: $ref: '#/components/schemas/KlondikeZone' # ------------------------------------------------------------------------- # FreeCell # ------------------------------------------------------------------------- FreeCellZone: type: object description: Specifies a zone on the FreeCell board for move commands required: - zone properties: zone: type: string description: "Zone name: tableau, freecell, or foundation" enum: [tableau, freecell, foundation] example: tableau col: type: integer description: Column index (0-based, used for tableau and foundation zones) example: 3 cell: type: integer description: Free cell index (0-based, used for freecell zone) example: 0 cardIndex: type: integer description: Index of the card within the column (for moving multiple cards from tableau) example: 0 FreeCellRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `move` (`m`), `giveup` (`g`), `hint` (`h`), `autocomplete` (`ac`), `undo` (`u`), `undo_n`, `log` (`l`). enum: [reset, r, move, m, giveup, g, hint, h, autocomplete, ac, undo, u, undo_n, log, l] example: move from: description: Source zone for a move command $ref: '#/components/schemas/FreeCellZone' to: description: Destination zone for a move command $ref: '#/components/schemas/FreeCellZone' n: type: integer description: Number of undos to perform (required when command is undo_n) example: 3 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 FreeCellResponse: type: object description: Current state of the FreeCell game required: - tableau - freeCells - foundation - phase - moveCount - canUndo - isStalemate - undoToEscape - message properties: tableau: type: array description: The 8 tableau columns, each containing an array of face-up cards items: type: array items: $ref: '#/components/schemas/Card' freeCells: type: array description: The 4 free cells (null or a Card) items: nullable: true $ref: '#/components/schemas/Card' foundation: type: array description: The 4 foundation piles (one per suit), each containing an array of cards items: type: array items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 0 = Playing, 1 = GameClear, 2 = GameOver" enum: [0, 1, 2] example: 0 moveCount: type: integer description: Total number of moves made in the current game example: 0 canUndo: type: boolean description: Whether the undo action is available example: false isStalemate: type: boolean description: Whether the game is in a stalemate (no moves can lead to a win) example: false undoToEscape: type: integer description: Number of undos needed to reach a non-stalemate state (0 if not stalemate, -1 if no escape) example: 0 hint: type: object description: Hint for the next available move (only present when hint command is used) properties: from: $ref: '#/components/schemas/FreeCellZone' to: $ref: '#/components/schemas/FreeCellZone' message: type: string description: Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Baccarat # ------------------------------------------------------------------------- BaccaratRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bet` (`b`), `log` (`l`), `clearhistory` (`ch`). enum: [r, reset, b, bet, log, l, ch, clearhistory] example: bet amount: type: integer description: | Bet amount. Required for `bet` command (minimum 10, must be a multiple of 10). Ignored for other commands. minimum: 10 example: 100 betType: type: integer description: | Bet type. Required for `bet` command. 0 = Player, 1 = Banker, 2 = Tie. enum: [0, 1, 2] example: 0 playerPairBet: type: integer description: | Side bet amount on Player Pair. Optional; used with the `bet` command. Ignored for other commands. minimum: 0 example: 50 bankerPairBet: type: integer description: | Side bet amount on Banker Pair. Optional; used with the `bet` command. Ignored for other commands. minimum: 0 example: 50 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 BaccaratResponse: type: object description: Current state of the Baccarat game required: - playerHand - bankerHand - playerHandValue - bankerHandValue - phase - chips - betAmount - betType - result - payout - message properties: playerHand: type: array description: Cards in the player's hand items: $ref: '#/components/schemas/Card' bankerHand: type: array description: Cards in the banker's hand items: $ref: '#/components/schemas/Card' playerHandValue: type: integer description: "Player hand value (ones digit of sum: 0-9)" example: 7 bankerHandValue: type: integer description: "Banker hand value (ones digit of sum: 0-9)" example: 5 phase: type: integer description: "Game phase: 1 = Bet, 2 = End" enum: [1, 2] example: 1 chips: type: integer description: Current chip count example: 1000 betAmount: type: integer description: Current bet amount example: 100 betType: type: integer description: "Current bet type: 0 = Player, 1 = Banker, 2 = Tie" enum: [0, 1, 2] example: 0 result: type: integer description: "Game result: 1 = Player wins, -1 = Banker wins, 0 = Tie/not resolved" enum: [-1, 0, 1] example: 0 payout: type: integer description: Payout amount for the current round example: 0 history: type: array description: | Big Road history — an ordered list of round results. Each element is an integer: 0 = Player win, 1 = Banker win, 2 = Tie. items: type: integer enum: [0, 1, 2] example: [0, 1, 1, 2, 0] playerPairBet: type: integer description: Current Player Pair side bet amount example: 0 bankerPairBet: type: integer description: Current Banker Pair side bet amount example: 0 sideBetResults: type: array description: Results of side bets for the current round items: type: object properties: betType: type: integer description: "Side bet type identifier" resultType: type: integer description: "Result type identifier" resultName: type: string description: "Human-readable result name" betAmount: type: integer description: "Amount wagered on this side bet" payout: type: integer description: "Payout amount (0 if lost)" message: type: string description: Game result or error message. Empty during bet phase. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Spades # ------------------------------------------------------------------------- SpadesRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bid` (`b`), `play` (`p`), `next` (`n`), `nextround` (`nr`), `log` (`l`). enum: [reset, r, bid, b, play, p, next, n, nextround, nr, log, l] example: reset bid: type: integer description: Bid value (0-13). Required for `bid` command. minimum: 0 maximum: 13 example: 3 cardIndex: type: integer description: Index of the card to play. Required for `play` command. example: 2 config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 1000 nilBonus: type: integer minimum: 0 maximum: 500 bagPenaltyThreshold: type: integer minimum: 1 maximum: 100 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 SpadesResponse: type: object description: Current state of the Spades game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' bid: type: integer roundScore: type: integer cumulativeScore: type: integer trickCount: type: integer bags: type: integer phase: type: integer description: "Game phase: 0 = Bid, 1 = Play, 2 = TrickEnd, 3 = RoundEnd, 4 = GameEnd" enum: [0, 1, 2, 3, 4] example: 0 roundNumber: type: integer description: Current round number trickNumber: type: integer description: Current trick number within the round currentPlayerIdx: type: integer description: Index of the player whose turn it is bidPlayerIdx: type: integer description: Index of the player currently bidding currentTrick: type: array description: Cards played in the current trick items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' spadesBroken: type: boolean description: Whether spades have been broken (played as a non-lead card) gameEndFlag: type: boolean description: Whether the game has ended winnerIdx: type: integer description: Index of the winning player (-1 if not ended) leadPlayerIdx: type: integer description: Index of the player who leads the current trick config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer nilBonus: type: integer bagPenaltyThreshold: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object nullable: true description: Recommended play or bid hint (only present when hint command is used) properties: cardIndex: type: integer nullable: true description: Recommended card index (null during bid phase) bid: type: integer nullable: true description: Recommended bid value (null during play phase) reason: type: string description: Machine-readable reason key for the hint # ------------------------------------------------------------------------- # Crazy Eights # ------------------------------------------------------------------------- CrazyEightsRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `play` (`p`), `draw` (`d`), `suit` (`s`), `nextround` (`nr`), `log` (`l`). enum: [reset, r, play, p, draw, d, suit, s, nextround, nr, log, l] example: reset cardIndex: type: integer description: Index of the card to play. Required for `play` command. example: 2 suit: type: integer description: "Suit choice after playing an 8 (1=♠, 2=♣, 3=♥, 4=♦). Required for `suit` command." minimum: 1 maximum: 4 example: 3 config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 1000 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 CrazyEightsResponse: type: object description: Current state of the Crazy Eights game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' roundScore: type: integer cumulativeScore: type: integer phase: type: integer description: "Game phase: 0 = Play, 1 = ChooseSuit, 2 = RoundEnd, 3 = GameEnd" enum: [0, 1, 2, 3] example: 0 roundNumber: type: integer description: Current round number currentPlayerIdx: type: integer description: Index of the player whose turn it is discardTop: description: The top card of the discard pile $ref: '#/components/schemas/Card' drawPileCount: type: integer description: Number of cards remaining in the draw pile chosenSuit: type: integer description: "Suit chosen after playing an 8 (0 if not applicable; 1=♠, 2=♣, 3=♥, 4=♦)" gameEndFlag: type: boolean description: Whether the match has ended winnerIdx: type: integer description: Index of the match winner (-1 if not ended) config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Gin Rummy # ------------------------------------------------------------------------- GinRummyRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `drawstock` (`ds`), `drawdiscard` (`dd`), `discard` (`d`), `knock` (`k`), `layoff` (`lo`), `nextround` (`nr`), `log` (`l`). enum: [reset, r, drawstock, ds, drawdiscard, dd, discard, d, knock, k, layoff, lo, nextround, nr, log, l] example: reset cardIndex: type: integer description: Index of the card to discard or knock with. Required for `discard` and `knock` commands. example: 2 cardIndices: type: array description: Card indices for `layoff` command. items: type: integer example: [0, 3] config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 1000 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 GinRummyResponse: type: object description: Current state of the Gin Rummy game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' roundScore: type: integer cumulativeScore: type: integer phase: type: integer description: "Game phase: 0 = Draw, 1 = Discard, 2 = Layoff, 3 = RoundEnd, 4 = GameEnd" enum: [0, 1, 2, 3, 4] example: 0 roundNumber: type: integer description: Current round number currentPlayerIdx: type: integer description: Index of the player whose turn it is discardTop: description: The top card of the discard pile $ref: '#/components/schemas/Card' drawPileCount: type: integer description: Number of cards remaining in the stock pile gameEndFlag: type: boolean description: Whether the match has ended winnerIdx: type: integer description: Index of the match winner (-1 if not ended) knockerIdx: type: integer description: Index of the player who knocked (-1 if not applicable) knockerMelds: type: array description: "Knocker's melds (array of card groups)" items: type: object properties: cards: type: array items: $ref: '#/components/schemas/Card' knockerDeadwood: type: array description: "Knocker's deadwood cards" items: $ref: '#/components/schemas/Card' isGin: type: boolean description: Whether the knock was a gin (0 deadwood) config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Canasta types # ------------------------------------------------------------------------- CanastaRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `drawstock` (`ds`), `drawdiscard` (`dd`), `meld` (`m`), `skipmeld` (`sm`), `discard` (`d`), `goout` (`go`), `nextround` (`nr`), `log` (`l`). enum: [reset, r, drawstock, ds, drawdiscard, dd, meld, m, skipmeld, sm, discard, d, goout, go, nextround, nr, log, l] example: reset cardIndex: type: integer description: Index of the card to discard. Required for `discard` command. example: 2 naturalPairIndices: type: array description: Indices of two natural cards matching the discard top. Required for `drawdiscard` command. items: type: integer example: [0, 1] meldGroups: type: array description: Groups of card indices to meld. Required for `meld` command. items: type: array items: type: integer example: [[0, 1, 2]] config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 100000 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 CanastaResponse: type: object description: Current state of the Canasta game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' melds: type: array items: type: object properties: cards: type: array items: $ref: '#/components/schemas/Card' isNatural: type: boolean isCanasta: type: boolean rank: type: integer red3Count: type: integer red3s: type: array items: $ref: '#/components/schemas/Card' roundScore: type: integer cumulativeScore: type: integer hasCanasta: type: boolean hasInitMeld: type: boolean phase: type: integer description: "Game phase: 0 = Draw, 1 = Meld, 2 = Discard, 3 = RoundEnd, 4 = GameEnd" enum: [0, 1, 2, 3, 4] example: 0 roundNumber: type: integer description: Current round number currentPlayerIdx: type: integer description: Index of the player whose turn it is discardTop: description: The top card of the discard pile $ref: '#/components/schemas/Card' drawPileCount: type: integer description: Number of cards remaining in the stock pile discardPileCount: type: integer description: Number of cards in the discard pile isFrozen: type: boolean description: Whether the discard pile is frozen gameEndFlag: type: boolean description: Whether the match has ended winnerIdx: type: integer description: Index of the match winner (-1 if not ended) config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Pinochle types # ------------------------------------------------------------------------- PinochleRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bid` (`b`), `pass` (`pa`), `trump` (`t`), `meld` (`m`), `play` (`p`), `next` (`n`), `nextround` (`nr`), `hint` (`h`), `log` (`l`). enum: [reset, r, bid, b, pass, pa, trump, t, meld, m, play, p, next, n, nextround, nr, hint, h, log, l] example: reset bidAmount: type: integer description: Bid amount. Required for `bid` command. Minimum 20. example: 25 suit: type: integer description: Trump suit (1=Spade, 2=Club, 3=Heart, 4=Diamond). Required for `trump` command. minimum: 1 maximum: 4 example: 3 cardIndex: type: integer description: Index of the card to play. Required for `play` command. example: 0 config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 10000 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 PinochleResponse: type: object description: Current state of the Pinochle game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' team: type: integer trickCount: type: integer bid: type: integer hasPassed: type: boolean meldScore: type: integer trickPoints: type: integer phase: type: integer roundNumber: type: integer trickNumber: type: integer currentPlayerIdx: type: integer bidPlayerIdx: type: integer dealerIdx: type: integer trumpSuit: type: integer highestBid: type: integer highestBidder: type: integer currentTrick: type: array items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' teamScores: type: array items: type: integer minItems: 2 maxItems: 2 gameEndFlag: type: boolean winnerTeam: type: integer leadPlayerIdx: type: integer playerMelds: type: array items: type: array items: type: object properties: type: type: integer points: type: integer cards: type: array items: $ref: '#/components/schemas/Card' validPlayIndices: type: array items: type: integer message: type: string messageCode: type: string messageParams: type: object additionalProperties: type: string config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer # ------------------------------------------------------------------------- # Spider Solitaire types # ------------------------------------------------------------------------- SpiderRequest: type: object required: - command - sessionId properties: command: type: string enum: [reset, deal, move, giveup, hint, autocomplete, undo, undo_n, log] n: type: integer description: Number of undos to perform (required when command is undo_n) example: 3 sessionId: type: string from: $ref: '#/components/schemas/SpiderZone' to: $ref: '#/components/schemas/SpiderZone' config: type: object properties: difficulty: type: integer enum: [1, 2, 4] description: "1=1-suit, 2=2-suit, 4=4-suit" SpiderZone: type: object required: - zone properties: zone: type: string enum: [tableau] col: type: integer cardIndex: type: integer SpiderTableauCard: type: object required: - faceUp properties: card: $ref: '#/components/schemas/Card' faceUp: type: boolean SpiderHint: type: object properties: fromCol: type: integer cardIndex: type: integer toCol: type: integer SpiderResponse: type: object required: - tableau - stockCount - completedSuits - phase - moveCount - canUndo - isStalemate - undoToEscape - score - difficulty - message properties: tableau: type: array items: type: array items: $ref: '#/components/schemas/SpiderTableauCard' stockCount: type: integer completedSuits: type: integer phase: type: integer enum: [0, 1, 2] description: "0=Playing, 1=GameClear, 2=GameOver" moveCount: type: integer canUndo: type: boolean isStalemate: type: boolean undoToEscape: type: integer description: Number of undos needed to reach a non-stalemate state (0 if not stalemate, -1 if no escape) example: 0 score: type: integer difficulty: type: integer enum: [1, 2, 4] hint: $ref: '#/components/schemas/SpiderHint' message: type: string messageCode: type: string messageParams: type: object additionalProperties: type: string # ------------------------------------------------------------------------- # Napoleon # ------------------------------------------------------------------------- NapoleonRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bid` (`b`), `trump` (`t`), `exchange` (`e`), `play` (`p`), `next` (`n`), `nextround` (`nr`), `hint` (`h`), `log` (`l`), `setdifficulty` (`sd`), `setlimit` (`sl`), `setminbid` (`sm`). enum: [reset, r, bid, b, trump, t, exchange, e, play, p, next, n, nextround, nr, hint, h, log, l, setdifficulty, sd, setlimit, sl, setminbid, sm] example: reset bid: type: integer description: Bid value (number of picture cards, or 0 to pass). Required for `bid` command. minimum: 0 maximum: 17 example: 12 suit: type: integer description: "Trump suit (1=Spade, 2=Clover, 3=Heart, 4=Diamond). Required for `trump` command." minimum: 1 maximum: 4 example: 1 cardIndex: type: integer description: Index of the card to play. Required for `play` command. example: 2 cardIndices: type: array items: type: integer description: Indices of cards to exchange with kitty. Required for `exchange` command. example: [0, 3] config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 1000 minBid: type: integer minimum: 1 maximum: 17 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 NapoleonResponse: type: object description: Current state of the Napoleon game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' bid: type: integer picCards: type: integer description: Number of picture cards captured cumulativeScore: type: integer isNapoleon: type: boolean isAdjutant: type: boolean phase: type: integer description: "Game phase: 0 = Bid, 1 = Trump, 2 = Exchange, 3 = Play, 4 = TrickEnd, 5 = RoundEnd, 6 = GameEnd" enum: [0, 1, 2, 3, 4, 5, 6] example: 0 roundNumber: type: integer description: Current round number trickNumber: type: integer description: Current trick number within the round currentPlayerIdx: type: integer description: Index of the player whose turn it is bidPlayerIdx: type: integer description: Index of the player currently bidding napoleonIdx: type: integer description: Index of the Napoleon player (-1 if not yet decided) adjutantCard: $ref: '#/components/schemas/Card' trumpSuit: type: integer description: "Declared trump suit (1=Spade, 2=Clover, 3=Heart, 4=Diamond, 0=not yet declared)" currentTrick: type: array description: Cards played in the current trick items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' kitty: type: array description: Kitty cards (visible to Napoleon during exchange phase) items: $ref: '#/components/schemas/Card' napoleonBid: type: integer description: Napoleon's winning bid napoleonPicCards: type: integer description: Total picture cards captured by Napoleon's team gameEndFlag: type: boolean description: Whether the game has ended winnerIdx: type: integer description: Index of the winning player (-1 if not ended) config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer minBid: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object nullable: true description: Recommended play hint (only present when hint command is used) properties: cardIndex: type: integer nullable: true description: Recommended card index reason: type: string description: Machine-readable reason key for the hint # ------------------------------------------------------------------------- # Indian Poker # ------------------------------------------------------------------------- IndianPokerRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `fold` (`f`), `check` (`ck`), `call` (`c`), `bet` (`b`), `raise` (`ra`), `allin` (`a`), `log` (`l`). enum: [reset, r, fold, f, check, ck, call, c, bet, b, raise, ra, allin, a, log, l] example: reset amount: type: integer description: Bet/raise amount. Required for `bet` and `raise` commands. minimum: 1 example: 50 humanPlayMs: type: integer description: Time in milliseconds the human took to decide (used for Meta-AI hesitation tracking). example: 3000 ante: type: integer description: Ante amount (only used with `reset`). minimum: 1 example: 10 bettingLimit: type: integer description: "Betting limit type (0=Fixed, 1=Pot Limit, 2=No Limit). Only used with `reset`." minimum: 0 maximum: 2 example: 2 cpuMetaAI: type: boolean description: "Whether CPU Meta-AI is enabled. Only used with `reset`." example: true sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 IndianPokerResponse: type: object description: Current state of the Indian Poker game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean card: $ref: '#/components/schemas/Card' nullable: true description: "Player's card (null for human during betting, visible for CPUs)" chips: type: integer currentBet: type: integer folded: type: boolean allIn: type: boolean playStyleName: type: string pot: type: integer description: Current pot size sidePots: type: array description: Side pots (when players are all-in with different amounts) items: type: object properties: amount: type: integer eligiblePlayers: type: array items: type: integer dealerIdx: type: integer description: Index of the current dealer currentTurn: type: integer description: Index of the player whose turn it is phase: type: integer description: "Game phase: 0 = Init, 1 = Ante, 2 = Betting, 3 = Showdown, 4 = End" enum: [0, 1, 2, 3, 4] example: 0 gameEndFlag: type: boolean description: Whether the game has ended lastBet: type: integer description: The last bet amount in the current round minRaise: type: integer description: Minimum raise amount raiseCount: type: integer description: Number of raises in the current round maxBetAmount: type: integer description: Maximum allowed bet amount (based on betting limit) bettingLimit: type: integer description: "Current betting limit (0=Fixed, 1=Pot Limit, 2=No Limit)" ante: type: integer description: Current ante amount handCount: type: integer description: Number of hands played roundResults: type: array description: Results from the showdown items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' cardRank: type: integer description: "Card rank (2-14, Ace=14)" wonAmount: type: integer cpuActions: type: array description: CPU actions taken during the betting round items: type: object properties: playerIdx: type: integer action: type: integer amount: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. VideoPokerRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bet` (`b`), `hold` (`h`), `log` (`l`). enum: [reset, r, bet, b, hold, h, log, l] example: reset amount: type: integer description: Bet amount (1-5 coins). Required for `bet` command. minimum: 1 maximum: 5 example: 1 indices: type: array description: Card positions to hold (0-4). Required for `hold` command. items: type: integer minimum: 0 maximum: 4 example: [0, 2, 4] sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 VideoPokerResponse: type: object description: Current state of the Video Poker game properties: hand: type: array description: The player's 5-card hand items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 1 = Bet, 2 = Draw, 3 = Result" enum: [1, 2, 3] example: 1 chips: type: integer description: Current chip count example: 100 betAmount: type: integer description: Current bet amount (1-5 coins) example: 1 result: type: integer description: "Round result: 1 = win, 0 = loss/push, -1 = not resolved" enum: [-1, 0, 1] example: 0 payout: type: integer description: Payout amount for the current round example: 0 handRank: type: integer description: "Hand rank code (0 = no qualifying hand, higher = better hand)" example: 0 handName: type: string description: Human-readable name of the evaluated hand rank example: "" heldIndices: type: array description: Indices of held cards (0-4) items: type: integer example: [] message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Euchre # ------------------------------------------------------------------------- EuchreRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `orderup` (`o`), `orderupalone` (`oa`), `pass` (`pa`), `calltrump` (`c`), `calltrumpalone` (`ca`), `discard` (`d`), `play` (`p`), `next` (`n`), `nextround` (`nr`), `hint` (`h`), `log` (`l`). enum: [reset, r, orderup, o, orderupalone, oa, pass, pa, calltrump, c, calltrumpalone, ca, discard, d, play, p, next, n, nextround, nr, hint, h, log, l] example: reset cardIndex: type: integer description: Index of the card to play or discard. Required for `play` and `discard` commands. example: 2 suit: type: integer description: "Trump suit to call (1=SPADE, 2=CLOVER, 3=HEART, 4=DIAMOND). Required for `calltrump` and `calltrumpalone` commands." minimum: 1 maximum: 4 example: 3 config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 pointLimit: type: integer minimum: 1 maximum: 100 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 EuchreResponse: type: object description: Current state of the Euchre game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' isDealer: type: boolean isAlone: type: boolean trickCount: type: integer teams: type: array description: Array of team objects (2 teams) items: type: object properties: score: type: integer roundScore: type: integer isMaker: type: boolean phase: type: integer description: "Game phase: 0 = PickUp, 1 = CallTrump, 2 = Discard, 3 = Play, 4 = TrickEnd, 5 = RoundEnd, 6 = GameEnd" enum: [0, 1, 2, 3, 4, 5, 6] example: 0 roundNumber: type: integer description: Current round number trickNumber: type: integer description: Current trick number within the round currentPlayerIdx: type: integer description: Index of the player whose turn it is dealerIdx: type: integer description: Index of the dealer turnedUpCard: $ref: '#/components/schemas/Card' trumpSuit: type: integer description: "Trump suit (1=SPADE, 2=CLOVER, 3=HEART, 4=DIAMOND, 0=not yet decided)" currentTrick: type: array description: Cards played in the current trick items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' gameEndFlag: type: boolean description: Whether the game has ended winnerTeamIdx: type: integer description: Index of the winning team (-1 if not ended) config: type: object properties: cpuDifficulty: type: integer pointLimit: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object nullable: true description: Recommended play hint (only present when hint command is used) properties: cardIndex: type: integer nullable: true description: Recommended card index reason: type: string description: Machine-readable reason key for the hint # ------------------------------------------------------------------------- # Pyramid # ------------------------------------------------------------------------- PyramidCard: type: object description: A card position in the Pyramid layout required: - zone properties: zone: type: string description: "Zone name: pyramid or waste" enum: [pyramid, waste] example: pyramid row: type: integer description: Row index (0-based, required for pyramid zone) example: 6 col: type: integer description: Column index (0-based, required for pyramid zone) example: 0 PyramidRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `draw` (`d`), `remove` (`rm`), `giveup` (`g`), `hint` (`h`), `undo` (`u`), `undo_n`, `log` (`l`). enum: [reset, r, draw, d, remove, rm, giveup, g, hint, h, undo, u, undo_n, log, l] example: remove card1: description: First card for a remove command $ref: '#/components/schemas/PyramidCard' card2: description: Second card for a remove command (pair removal) $ref: '#/components/schemas/PyramidCard' n: type: integer description: Number of undos to perform (required when command is undo_n) example: 3 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 PyramidOutputCard: type: object description: A card in the Pyramid layout with its state properties: card: description: Card details (null when removed) $ref: '#/components/schemas/Card' removed: type: boolean description: Whether the card has been removed example: false exposed: type: boolean description: Whether the card is exposed (can be selected) example: true PyramidResponse: type: object description: Current state of the Pyramid game required: - pyramid - stockCount - waste - phase - moveCount - canUndo - isStalemate - undoToEscape - message properties: pyramid: type: array description: The 7 pyramid rows, each containing an array of cards (row i has i+1 cards) items: type: array items: $ref: '#/components/schemas/PyramidOutputCard' stockCount: type: integer description: Number of cards remaining in the stock pile example: 24 waste: type: array description: Cards in the waste pile (top card is the last element) items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 0 = Playing, 1 = GameClear, 2 = GameOver" enum: [0, 1, 2] example: 0 moveCount: type: integer description: Total number of moves made in the current game example: 0 canUndo: type: boolean description: Whether the undo action is available example: false isStalemate: type: boolean description: Whether the game is in a stalemate (no moves can lead to a win) example: false undoToEscape: type: integer description: Number of undos needed to reach a non-stalemate state (0 if not stalemate, -1 if no escape) example: 0 message: type: string description: Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object description: Hint for the next available move (only present when hint command is used) properties: type: type: string description: "Hint type: pair, king, waste_pair, waste_king" enum: [pair, king, waste_pair, waste_king] row1: type: integer description: Row of first card (-1 for waste operations) col1: type: integer description: Column of first card (-1 for waste operations) row2: type: integer description: Row of second card (-1 for king/waste operations) col2: type: integer description: Column of second card (-1 for king/waste operations) TriPeaksRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `draw` (`d`), `remove` (`rm`), `giveup` (`g`), `hint` (`h`), `undo` (`u`), `undo_n`, `log` (`l`). enum: [reset, r, draw, d, remove, rm, giveup, g, hint, h, undo, u, undo_n, log, l] example: remove row: type: integer description: Row index of the tableau card to remove (0-based) example: 3 col: type: integer description: Column index of the tableau card to remove (0-based) example: 0 n: type: integer description: Number of undos to perform (required when command is undo_n) example: 3 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 TriPeaksOutputCard: type: object description: A card in the TriPeaks tableau with its state properties: card: description: Card details (null when removed) $ref: '#/components/schemas/Card' removed: type: boolean description: Whether the card has been removed example: false exposed: type: boolean description: Whether the card is exposed (can be selected) example: true TriPeaksResponse: type: object description: Current state of the TriPeaks game required: - tableau - stockCount - waste - phase - moveCount - canUndo - isStalemate - undoToEscape - message properties: tableau: type: array description: The tableau rows, each containing an array of cards (three overlapping peaks) items: type: array items: $ref: '#/components/schemas/TriPeaksOutputCard' stockCount: type: integer description: Number of cards remaining in the stock pile example: 23 waste: type: array description: Cards in the waste pile (top card is the last element) items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 0 = Playing, 1 = GameClear, 2 = GameOver" enum: [0, 1, 2] example: 0 moveCount: type: integer description: Total number of moves made in the current game example: 0 canUndo: type: boolean description: Whether the undo action is available example: false isStalemate: type: boolean description: Whether the game is in a stalemate (no moves can lead to a win) example: false undoToEscape: type: integer description: Number of undos needed to reach a non-stalemate state (0 if not stalemate, -1 if no escape) example: 0 message: type: string description: Game result or status message. Empty while the game is in progress. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object description: Hint for the next available move (only present when hint command is used) properties: row: type: integer description: Row of the card to remove col: type: integer description: Column of the card to remove CribbageRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `discard` (`d`), `peg` (`p`), `go`, `shownext` (`sn`), `nextround` (`nr`), `setdifficulty` (`sd`), `setlimit` (`sl`), `log` (`l`). enum: [reset, r, discard, d, peg, p, go, shownext, sn, nextround, nr, setdifficulty, sd, setlimit, sl, log, l] example: discard cardIndex: type: integer description: Card index for peg command example: 0 cardIndices: type: array items: type: integer description: Card indices for discard command (exactly 2 indices) example: [4, 5] config: type: object description: Game configuration (applied on reset) properties: cpuDifficulty: type: integer description: "CPU difficulty: 0 = Easy, 1 = Normal, 2 = Hard" enum: [0, 1, 2] example: 1 pointLimit: type: integer description: Point limit to win (default 121) example: 121 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 CribbagePlayer: type: object description: A player in the Cribbage game required: - id - isHuman - cardCount - cards - roundScore - cumulativeScore properties: id: type: integer description: Player index (0 = human, 1 = CPU) example: 0 isHuman: type: boolean description: Whether this player is human example: true cardCount: type: integer description: Number of cards in hand example: 6 cards: type: array description: Player's hand cards (visible to human player; hidden for CPU) items: $ref: '#/components/schemas/Card' roundScore: type: integer description: Points scored in the current round example: 0 cumulativeScore: type: integer description: Cumulative score across all rounds example: 0 CribbageScoreDetail: type: object description: Breakdown of a hand/crib score properties: fifteens: type: integer description: Points from combinations summing to 15 example: 4 pairs: type: integer description: Points from pairs example: 2 runs: type: integer description: Points from runs example: 3 flush: type: integer description: Points from flush example: 0 nobs: type: integer description: Points from nobs (Jack of starter suit) example: 1 total: type: integer description: Total points for this hand/crib example: 10 CribbageResponse: type: object description: Current state of the Cribbage game required: - players - phase - roundNumber - currentPlayerIdx - dealerIdx - crib - pegCount - pegPlayedCards - showPhaseStep - handScoreDetails - gameEndFlag - winnerIdx - message properties: players: type: array description: Array of 2 players (index 0 = human, index 1 = CPU) items: $ref: '#/components/schemas/CribbagePlayer' phase: type: integer description: "Game phase: 0 = Discard, 1 = Cut, 2 = Pegging, 3 = Show, 4 = RoundEnd, 5 = GameEnd" enum: [0, 1, 2, 3, 4, 5] example: 0 roundNumber: type: integer description: Current round number example: 1 currentPlayerIdx: type: integer description: Index of the current player example: 0 dealerIdx: type: integer description: Index of the dealer for this round example: 1 crib: type: array description: Cards in the crib (visible only during Show/RoundEnd/GameEnd phases) items: $ref: '#/components/schemas/Card' starter: description: The starter (cut) card (null before cut phase) $ref: '#/components/schemas/Card' pegCount: type: integer description: Current pegging count (0-31) example: 0 pegPlayedCards: type: array description: Cards played in the current pegging sequence items: $ref: '#/components/schemas/Card' showPhaseStep: type: integer description: "Show phase step: 0 = non-dealer hand, 1 = dealer hand, 2 = crib" enum: [0, 1, 2] example: 0 handScoreDetails: type: array description: Score details for show phase [non-dealer hand, dealer hand, crib] items: $ref: '#/components/schemas/CribbageScoreDetail' gameEndFlag: type: boolean description: Whether the game has ended example: false winnerIdx: type: integer description: Index of the winner (-1 if game not ended) example: -1 message: type: string description: Game status or result message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. config: type: object description: Current game configuration properties: cpuDifficulty: type: integer description: "CPU difficulty: 0 = Easy, 1 = Normal, 2 = Hard" example: 1 pointLimit: type: integer description: Point limit to win example: 121 # ------------------------------------------------------------------------- # Three Card Poker # ------------------------------------------------------------------------- ThreeCardRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bet` (`b`), `play` (`p`), `fold` (`f`), `log` (`l`). enum: [r, reset, b, bet, p, play, f, fold, log, l] example: bet amount: type: integer description: | Ante bet amount. Required for `bet` command (minimum 10, must be a multiple of 10). Ignored for other commands. minimum: 10 example: 100 pairPlusBet: type: integer description: | Pair Plus side bet amount. Optional; used with the `bet` command. 0 or omitted means no side bet. minimum: 0 example: 50 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 ThreeCardResponse: type: object description: Current state of the Three Card Poker game required: - playerHand - dealerHand - phase - chips - anteBet - pairPlusBet - playBet - result - antePayout - playPayout - anteBonusPayout - pairPlusPayout - totalPayout - dealerQualified - playerHandRank - dealerHandRank - message properties: playerHand: type: array description: Cards in the player's hand (3 cards) items: $ref: '#/components/schemas/Card' dealerHand: type: array description: Cards in the dealer's hand (3 cards; hidden until End phase) items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 1 = Bet, 2 = Action, 3 = End" enum: [1, 2, 3] example: 1 chips: type: integer description: Current chip count example: 1000 anteBet: type: integer description: Current Ante bet amount example: 100 pairPlusBet: type: integer description: Current Pair Plus side bet amount example: 50 playBet: type: integer description: Current Play bet amount (equal to Ante when played) example: 0 result: type: integer description: "Game result: 1 = Player wins, -1 = Dealer wins, 0 = Push/not resolved" enum: [-1, 0, 1] example: 0 antePayout: type: integer description: Payout from the Ante bet example: 0 playPayout: type: integer description: Payout from the Play bet example: 0 anteBonusPayout: type: integer description: Ante Bonus payout (Straight Flush 5:1, Three of a Kind 4:1, Straight 1:1) example: 0 pairPlusPayout: type: integer description: Pair Plus side bet payout example: 0 totalPayout: type: integer description: Total payout for the current round (sum of all payouts) example: 0 dealerQualified: type: boolean description: Whether the dealer qualified (Q-high or better) example: false playerHandRank: type: integer description: Player's hand rank (1=High Card, 2=Pair, 3=Flush, 4=Straight, 5=Three of a Kind, 6=Straight Flush) example: 2 dealerHandRank: type: integer description: Dealer's hand rank (1=High Card, 2=Pair, 3=Flush, 4=Straight, 5=Three of a Kind, 6=Straight Flush) example: 1 message: type: string description: Game result or error message. Empty during bet/action phase. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Oh Hell # ------------------------------------------------------------------------- OhHellRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bid` (`b`), `play` (`p`), `next` (`n`), `nextround` (`nr`), `hint` (`h`), `log` (`l`). enum: [reset, r, bid, b, play, p, next, n, nextround, nr, hint, h, log, l] example: reset bid: type: integer description: Bid value (0 to current hand size). Required for `bid` command. minimum: 0 example: 2 cardIndex: type: integer description: Index of the card to play. Required for `play` command. example: 2 config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 maxHandSize: type: integer minimum: 1 maximum: 13 scoringVariant: type: integer minimum: 0 maximum: 1 roundDirection: type: integer minimum: 0 maximum: 1 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 OhHellResponse: type: object description: Current state of the Oh Hell game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' bid: type: integer roundScore: type: integer cumulativeScore: type: integer trickCount: type: integer phase: type: integer description: "Game phase: 0 = Bid, 1 = Play, 2 = TrickEnd, 3 = RoundEnd, 4 = GameEnd" enum: [0, 1, 2, 3, 4] example: 0 roundNumber: type: integer description: Current round number totalRounds: type: integer description: Total number of rounds in the game handSize: type: integer description: Number of cards dealt to each player this round trickNumber: type: integer description: Current trick number within the round currentPlayerIdx: type: integer description: Index of the player whose turn it is bidPlayerIdx: type: integer description: Index of the player currently bidding dealerIdx: type: integer description: Index of the dealer for the current round currentTrick: type: array description: Cards played in the current trick items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' trumpCard: nullable: true description: The card determining the trump suit (null if no trump) allOf: - $ref: '#/components/schemas/Card' trumpSuit: type: integer description: "Trump suit, same enum as contractSuit (0=not determined, 1=Clubs, 2=Diamonds, 3=Hearts, 4=Spades, 5=NoTrump)" gameEndFlag: type: boolean description: Whether the game has ended winnerIdx: type: integer description: Index of the winning player (-1 if not ended) leadPlayerIdx: type: integer description: Index of the player who leads the current trick restrictedBid: type: integer description: The bid value the dealer cannot choose (-1 if no restriction) config: type: object properties: cpuDifficulty: type: integer maxHandSize: type: integer scoringVariant: type: integer roundDirection: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object nullable: true description: Recommended play or bid hint (only present when hint command is used) properties: cardIndex: type: integer nullable: true description: Recommended card index (null during bid phase) bid: type: integer nullable: true description: Recommended bid value (null during play phase) reason: type: string description: Machine-readable reason key for the hint # ------------------------------------------------------------------------- # Contract Bridge # ------------------------------------------------------------------------- BridgeRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `bid` (`b`), `play` (`p`), `next` (`n`), `nextround` (`nr`), `hint` (`h`), `log` (`l`). enum: [reset, r, bid, b, play, p, next, n, nextround, nr, hint, h, log, l] example: reset bidType: type: integer description: | Bid type. Required for `bid` command. 0 = Pass, 1 = Normal bid (requires bidLevel + bidSuit), 2 = Double, 3 = Redouble. minimum: 0 maximum: 3 example: 1 bidLevel: type: integer description: "Bid level (1-7). Required when bidType = 1." minimum: 1 maximum: 7 example: 1 bidSuit: type: integer description: "Bid suit (1=Clubs, 2=Diamonds, 3=Hearts, 4=Spades, 5=NoTrump). Required when bidType = 1." minimum: 1 maximum: 5 example: 5 cardIndex: type: integer description: Index of the card to play. Required for `play` command. example: 2 config: type: object description: Optional game configuration (only used with `reset`). properties: cpuDifficulty: type: integer minimum: 0 maximum: 2 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 BridgeResponse: type: object description: Current state of the Contract Bridge game properties: players: type: array description: Array of player objects items: type: object properties: id: type: integer isHuman: type: boolean cardCount: type: integer cards: type: array items: $ref: '#/components/schemas/Card' trickCount: type: integer teams: type: array description: Array of team objects (2 teams) items: type: object properties: belowLine: type: integer description: Points below the line (towards game) aboveLine: type: integer description: Points above the line (bonuses/penalties) gamesWon: type: integer description: Number of games won in the rubber vulnerable: type: boolean description: Whether the team is vulnerable phase: type: integer description: "Game phase: 0 = Bid, 1 = Play, 2 = TrickEnd, 3 = RoundEnd, 4 = GameEnd" enum: [0, 1, 2, 3, 4] example: 0 roundNumber: type: integer description: Current round (deal) number trickNumber: type: integer description: Current trick number within the round currentPlayerIdx: type: integer description: Index of the player whose turn it is dealerIdx: type: integer description: Index of the dealer declarerIdx: type: integer description: Index of the declarer (-1 if not yet determined) dummyIdx: type: integer description: Index of the dummy (-1 if not yet determined) contractLevel: type: integer description: Contract level (1-7, 0 if not yet determined) contractSuit: type: integer description: "Contract suit (1=Clubs, 2=Diamonds, 3=Hearts, 4=Spades, 5=NoTrump, 0=not yet determined)" contractDoubled: type: integer description: "Double state (0=none, 1=doubled, 2=redoubled)" trumpSuit: type: integer description: "Trump suit (1=Spade, 2=Clover, 3=Heart, 4=Diamond, -1=NoTrump, 0=not yet determined)" currentTrick: type: array description: Cards played in the current trick items: type: object properties: playerIdx: type: integer card: $ref: '#/components/schemas/Card' bidHistory: type: array description: Auction bid history items: type: object properties: playerIdx: type: integer bidType: type: integer level: type: integer suit: type: integer gameEndFlag: type: boolean description: Whether the game (rubber) has ended winnerTeamIdx: type: integer description: Index of the winning team (-1 if not ended) config: type: object properties: cpuDifficulty: type: integer message: type: string description: Game status message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. hint: type: object nullable: true description: Recommended play or bid hint (only present when hint command is used) properties: cardIndex: type: integer nullable: true description: Recommended card index (null during bid phase) bidType: type: integer nullable: true description: Recommended bid type (null during play phase) bidLevel: type: integer nullable: true description: Recommended bid level (null during play phase) bidSuit: type: integer nullable: true description: Recommended bid suit (null during play phase) reason: type: string description: Machine-readable reason key for the hint # ------------------------------------------------------------------------- # Pineapple Poker # ------------------------------------------------------------------------- PineappleRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `fold` (`f`), `check` (`ck`), `call` (`c`), `bet` (`b`), `raise` (`ra`), `allin` (`a`), `discard` (`d`), `rebuy` (`rb`), `skiprebuy` (`sr`), `addon` (`ad`), `skipaddon` (`sa`), `muck` (`m`), `show` (`sh`), `quit` (`q`). enum: [r, reset, f, fold, ck, check, c, call, b, bet, ra, raise, a, allin, d, discard, rb, rebuy, sr, skiprebuy, ad, addon, sa, skipaddon, m, muck, sh, show, log, q, quit] example: bet amount: type: integer description: | Bet/raise amount (for the `bet` and `raise` commands). Ignored for other commands. minimum: 1 example: 40 cardIdx: type: integer description: | Index of the hole card to discard (for the `discard` command). Must be 0, 1, or 2 (index into the 3 hole cards). minimum: 0 maximum: 2 example: 1 smallBlind: type: integer description: | Small blind amount (for the `reset` command). Defaults to 5 when omitted. minimum: 1 example: 5 bigBlind: type: integer description: | Big blind amount (for the `reset` command). Defaults to 10 when omitted. minimum: 1 example: 10 tournamentMode: type: boolean description: | Enable tournament mode with blind escalation (for the `reset` command). Defaults to false when omitted. example: false blindLevelHands: type: integer description: | Number of hands before blinds increase in tournament mode. Defaults to 10. minimum: 1 example: 10 blindMultiplier: type: integer description: | Blind multiplier as a percentage when blinds increase. Defaults to 200 (= 2x). minimum: 101 example: 200 bettingLimit: type: integer description: | Betting limit type (used with `reset` command). 0 = Fixed (default), 1 = Pot Limit, 2 = No Limit. minimum: 0 maximum: 2 example: 0 tableSize: type: integer description: | Table size (used with `reset` command). 4 = 4-max (default), 6 = 6-max, 9 = 9-max. enum: [4, 6, 9] example: 4 rebuyEnabled: type: boolean description: | Enable rebuy (used with `reset` command). Defaults to false when omitted. example: false addonEnabled: type: boolean description: | Enable addon (used with `reset` command). Defaults to false when omitted. example: false isDiscardPhase: type: boolean description: | Indicates whether the client is in the discard phase. Used internally to track discard state. example: false discardDone: type: boolean description: | Indicates whether the discard has been completed. Used internally to track discard completion. example: false sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 PineappleResponse: type: object required: - players - communityCards - pot - sidePots - dealerIdx - currentTurn - phase - gameEndFlag - lastBet - minRaise - roundResults - cpuActions - message properties: players: type: array description: State of all players (4/6/9 depending on table size) items: $ref: '#/components/schemas/HoldemPlayer' communityCards: type: array description: Community cards on the board (0/3/4/5 cards based on phase) items: $ref: '#/components/schemas/Card' pot: type: integer description: Current main pot size minimum: 0 example: 30 sidePots: type: array description: Side pots (created when players go all-in with different amounts) items: $ref: '#/components/schemas/HoldemSidePot' dealerIdx: type: integer description: Index of the dealer for this round example: 0 currentTurn: type: integer description: Index of the player whose turn it is example: 0 phase: type: integer description: | Current game phase: - `0` — Init - `1` — PreFlop - `2` — Flop - `3` — Turn - `4` — River - `5` — Showdown - `6` — End - `7` — Rebuy/Addon - `8` — Discard enum: [0, 1, 2, 3, 4, 5, 6, 7, 8] example: 1 gameEndFlag: type: boolean description: Whether the round has ended example: false lastBet: type: integer description: The current highest bet in this betting round minimum: 0 example: 10 minRaise: type: integer description: Minimum raise amount minimum: 0 example: 20 bettingLimit: type: integer description: | Betting limit type: 0 = Fixed, 1 = Pot Limit, 2 = No Limit. enum: [0, 1, 2] raiseCount: type: integer description: Number of raises in the current betting round. minimum: 0 maxBetAmount: type: integer description: | Maximum allowed bet/raise amount for Pot Limit mode. 0 means no limit (Fixed or No Limit mode). minimum: 0 roundResults: type: array description: Results for each non-folded player at showdown items: $ref: '#/components/schemas/HoldemResult' cpuActions: type: array description: CPU actions that occurred since the last human action items: $ref: '#/components/schemas/HoldemCpuAction' isDiscardPhase: type: boolean description: Whether the game is currently in the discard phase (phase=8) example: false discardDone: type: boolean description: Whether the human player has completed discarding example: false message: type: string description: | Game result or status message. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. handCount: type: integer description: Number of hands played (for tournament mode display) minimum: 0 example: 0 smallBlind: type: integer description: Current small blind amount minimum: 1 example: 5 bigBlind: type: integer description: Current big blind amount minimum: 2 example: 10 tournamentMode: type: boolean description: Whether tournament mode (blind escalation) is active example: false tableSize: type: integer description: Current table size (4/6/9) enum: [4, 6, 9] example: 4 muckAvailable: type: boolean description: Whether the human player can muck (hide) their hand at showdown example: false equity: $ref: '#/components/schemas/HoldemEquity' potOdds: type: number format: double description: Pot odds percentage (0-100). example: 33.33 # ------------------------------------------------------------------------- # Speed # ------------------------------------------------------------------------- SpeedRequest: type: object required: - command - sessionId properties: command: type: string description: Game command enum: [reset, r, play, p, flip, f, hint, h, log, l, quit, q] example: play cardIndex: type: integer description: Hand index of the card to play (required for `play` command) minimum: 0 example: 2 pileIndex: type: integer description: Center pile index (0 or 1) to play onto (required for `play` command) minimum: 0 maximum: 1 example: 0 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 SpeedResponse: type: object description: Current state of the Speed game required: - players - centerPiles - phase - gameEndFlag - winnerIdx - config - message properties: players: type: array description: Both players (index 0 = human, index 1 = CPU) items: type: object properties: id: type: integer description: Player index isHuman: type: boolean description: Whether this player is human cardCount: type: integer description: Number of cards in hand cards: type: array description: Cards in hand (only populated for human player) items: $ref: '#/components/schemas/Card' drawPileSize: type: integer description: Number of cards remaining in the draw pile centerPiles: type: array description: The two center piles (top card of each) items: $ref: '#/components/schemas/Card' phase: type: integer description: "Game phase: 0 = Play, 1 = Stuck, 2 = GameEnd" enum: [0, 1, 2] example: 0 gameEndFlag: type: boolean description: Whether the game has ended winnerIdx: type: integer description: Index of the winning player (-1 if game is not over) example: -1 cpuActions: type: array description: CPU play actions that occurred since the last human action items: type: object properties: cardIndex: type: integer pileIndex: type: integer hint: type: object description: Hint for the human player (present when hint command is used) properties: cardIndex: type: integer pileIndex: type: integer found: type: boolean config: type: object description: Current game configuration properties: cpuDifficulty: type: integer description: "CPU difficulty: 0 = Easy, 1 = Normal, 2 = Hard" message: type: string description: Game status or result message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Go Fish # ------------------------------------------------------------------------- GoFishRequest: type: object required: - command - sessionId properties: command: type: string description: Game command enum: [reset, r, ask, log, l] example: ask targetIdx: type: integer description: Target player index to ask (required for `ask` command) minimum: 0 maximum: 3 example: 1 rank: type: integer description: Card rank to ask for (1=Ace, 2-10, 11=J, 12=Q, 13=K; required for `ask` command) minimum: 1 maximum: 13 example: 7 config: type: object description: Game configuration (for `reset` command) properties: cpuDifficulty: type: integer description: CPU difficulty (0=Easy, 1=Normal, 2=Hard) minimum: 0 maximum: 2 example: 1 sessionId: type: string description: Client-generated session identifier (max 256 characters) example: my-session-001 GoFishResponse: type: object description: Current state of the Go Fish game required: - players - phase - gameEndFlag - winnerIdx - stockCount - config - message properties: players: type: array description: All 4 players (index 0 = human, 1-3 = CPU) items: type: object properties: id: type: integer description: Player index isHuman: type: boolean description: Whether this player is human cardCount: type: integer description: Number of cards in hand cards: type: array description: Cards in hand (only populated for human player) items: $ref: '#/components/schemas/Card' bookCount: type: integer description: Number of completed books books: type: array description: Completed books (array of 4-card groups) items: type: array items: $ref: '#/components/schemas/Card' isFinished: type: boolean description: Whether this player has finished (no cards and no more draws) phase: type: integer description: "Game phase: 0 = Play, 1 = GameEnd" enum: [0, 1] example: 0 gameEndFlag: type: boolean description: Whether the game has ended winnerIdx: type: integer description: Index of the winning player (-1 if game is not over) example: -1 currentTurn: type: integer description: Index of the player whose turn it is example: 0 stockCount: type: integer description: Number of cards remaining in the stock pile example: 32 humanAction: type: object description: The result of the human player's last ask action properties: askPlayerIdx: type: integer askTargetIdx: type: integer askRank: type: integer success: type: boolean cardsReceived: type: integer bookFormed: type: boolean bookRank: type: integer cpuActions: type: array description: CPU actions that occurred since the last human action items: type: object properties: askPlayerIdx: type: integer askTargetIdx: type: integer askRank: type: integer success: type: boolean cardsReceived: type: integer bookFormed: type: boolean bookRank: type: integer config: type: object description: Current game configuration properties: cpuDifficulty: type: integer description: "CPU difficulty: 0 = Easy, 1 = Normal, 2 = Hard" cpuMetaAI: type: boolean description: Whether Meta-AI is enabled message: type: string description: Game status or result message example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. # ------------------------------------------------------------------------- # Seven Card Stud # ------------------------------------------------------------------------- SevenCardStudRequest: type: object required: - command - sessionId properties: command: type: string description: | Game command. Accepted values: `reset` (`r`), `fold` (`f`), `check` (`ck`), `call` (`c`), `bet` (`b`), `raise` (`ra`), `allin` (`a`), `rebuy` (`rb`), `skiprebuy` (`sr`), `addon` (`ad`), `skipaddon` (`sa`), `muck` (`m`), `show` (`sh`), `log` (`l`), `quit` (`q`). enum: [r, reset, f, fold, ck, check, c, call, b, bet, ra, raise, a, allin, rb, rebuy, sr, skiprebuy, ad, addon, sa, skipaddon, m, muck, sh, show, log, l, q, quit] example: bet amount: type: integer description: | Bet/raise amount (for the `bet` and `raise` commands). Ignored for other commands. minimum: 1 example: 40 humanPlayMs: type: integer description: Human play hesitation time in milliseconds (0=not measured) example: 1200 ante: type: integer description: | Ante amount (for the `reset` command). Defaults to 2 when omitted. minimum: 1 example: 2 bringIn: type: integer description: | Bring-in amount (for the `reset` command). Defaults to 5 when omitted. minimum: 1 example: 5 smallBet: type: integer description: | Small bet amount used on Third/Fourth Street (for the `reset` command). Defaults to 10 when omitted. minimum: 1 example: 10 bigBet: type: integer description: | Big bet amount used on Fifth/Sixth/Seventh Street (for the `reset` command). Defaults to 20 when omitted. minimum: 1 example: 20 bettingLimit: type: integer description: | Betting limit type (used with `reset` command). 0 = Fixed (default), 1 = Pot Limit, 2 = No Limit. minimum: 0 maximum: 2 example: 0 tableSize: type: integer description: | Table size (used with `reset` command). 2 through 7 players. Defaults to 4. minimum: 2 maximum: 7 example: 4 tournamentMode: type: boolean description: | Enable tournament mode with ante escalation (for the `reset` command). Defaults to false when omitted. example: false anteLevelHands: type: integer description: | Number of hands before ante increases in tournament mode (for the `reset` command). Defaults to 10 when omitted. Must be >= 1. minimum: 1 example: 10 anteMultiplier: type: integer description: | Ante multiplier as a percentage when ante increases (for the `reset` command). Defaults to 200 (= 2x). Must be >= 101. minimum: 101 example: 200 rebuyEnabled: type: boolean description: | Enable rebuy (used with `reset` command). Defaults to false when omitted. example: false rebuyMaxCount: type: integer description: | Maximum number of rebuys per player (used with `reset` command). Defaults to 3 when omitted. minimum: 1 example: 3 rebuyChips: type: integer description: | Chips granted per rebuy (used with `reset` command). Defaults to 1000 when omitted. minimum: 1 example: 1000 rebuyPeriodHands: type: integer description: | Number of hands during which rebuy is available (used with `reset` command). Defaults to 10 when omitted. minimum: 1 example: 10 addonEnabled: type: boolean description: | Enable addon (used with `reset` command). Defaults to false when omitted. example: false addonChips: type: integer description: | Chips granted by addon (used with `reset` command). Defaults to 1500 when omitted. minimum: 1 example: 1500 addonAfterHand: type: integer description: | Hand number after which addon becomes available (used with `reset` command). Defaults to 10 when omitted. minimum: 1 example: 10 cpuMetaAI: type: boolean description: | When true, CPUs adaptively adjust bluff and call behavior based on session-scoped game history (Meta-AI). Defaults to false. default: false example: false profile: type: object description: Human player profile for Meta-AI (sent back by the client to persist across sessions) sessionId: type: string description: Unique session identifier (max 256 characters) maxLength: 256 example: my-session-001 SevenCardStudPlayer: type: object required: - id - isHuman - holeCards - doorCards - chips - currentBet - folded - allIn - handRank - handName - bestHand - playStyleName properties: id: type: integer description: Player index (0 = human, 1+ = CPU) example: 0 isHuman: type: boolean description: Whether this is the human player example: true holeCards: type: array description: | Face-down hole cards (2 initially, 3 after Seventh Street). Human player's cards are always visible. CPU cards are hidden (empty array) until showdown. items: $ref: '#/components/schemas/Card' doorCards: type: array description: | Face-up door cards (1 on Third Street, up to 4 by Sixth Street). Visible to all players. items: $ref: '#/components/schemas/Card' chips: type: integer description: Current chip count minimum: 0 example: 990 currentBet: type: integer description: Current round bet amount minimum: 0 example: 10 folded: type: boolean description: Whether the player has folded example: false allIn: type: boolean description: Whether the player is all-in example: false handRank: type: integer description: | Numeric hand rank (only populated at showdown for non-folded players): 0 = High Card, 1 = One Pair, 2 = Two Pair, 3 = Three of a Kind, 4 = Straight, 5 = Flush, 6 = Full House, 7 = Four of a Kind, 8 = Straight Flush, 9 = Royal Flush. minimum: 0 maximum: 9 example: 0 handName: type: string description: Human-readable hand name (empty until showdown) example: "" bestHand: type: array description: Best 5-card hand (only populated at showdown) items: $ref: '#/components/schemas/Card' playStyleName: type: string description: | CPU play style name: TAG (Tight-Aggressive), LAP (Loose-Passive), TAP (Tight-Passive), LAG (Loose-Aggressive), GTO (Game Theory Optimal). Empty for human player. example: TAG totalHands: type: integer description: Total number of hands played (for HUD stats) minimum: 0 example: 10 vpip: type: integer description: VPIP percentage (Voluntarily Put $ In Pot) minimum: 0 maximum: 100 example: 50 pfr: type: integer description: PFR percentage (Pre-Flop Raise) minimum: 0 maximum: 100 example: 25 threeBet: type: integer description: 3Bet percentage minimum: 0 maximum: 100 example: 10 af: type: string description: "Aggression Factor (postflop bets+raises / calls). Values: decimal (e.g. \"2.5\"), \"inf\" (bets but no calls), \"-\" (no actions)" example: "2.5" SevenCardStudCpuAction: type: object description: Record of a CPU player's betting action required: - playerIdx - action - amount properties: playerIdx: type: integer description: Index of the CPU player who acted example: 1 action: type: integer description: | Action taken: 0 = Fold, 1 = Check, 2 = Call, 3 = Bet, 4 = Raise, 5 = All-in. enum: [0, 1, 2, 3, 4, 5] example: 2 amount: type: integer description: Amount bet/raised (0 for fold/check/call/all-in) example: 0 SevenCardStudResult: type: object description: Result for a player at showdown required: - playerIdx - handRank - handName - bestHand - wonAmount properties: playerIdx: type: integer description: Index of the player example: 0 handRank: type: integer description: Numeric hand rank example: 1 handName: type: string description: Hand name (e.g., "One Pair") example: One Pair kickers: type: string description: Kicker card values formatted as display string bestHand: type: array description: Best 5-card hand items: $ref: '#/components/schemas/Card' wonAmount: type: integer description: Chips won from the pot example: 40 mucked: type: boolean description: Whether the player mucked (hid) their hand at showdown example: false SevenCardStudSidePot: type: object description: A side pot created when a player goes all-in required: - amount - eligiblePlayers properties: amount: type: integer description: Total chips in this side pot example: 100 eligiblePlayers: type: array description: Indices of players eligible to win this pot items: type: integer example: [0, 1, 2] SevenCardStudResponse: type: object required: - players - pot - sidePots - dealerIdx - currentTurn - phase - gameEndFlag - lastBet - minRaise - roundResults - cpuActions - message properties: players: type: array description: State of all players (2-7 depending on table size) items: $ref: '#/components/schemas/SevenCardStudPlayer' communityCard: nullable: true description: Used as a shared community card only when the deck runs out of cards, otherwise null pot: type: integer description: Current main pot size minimum: 0 example: 30 sidePots: type: array description: Side pots (created when players go all-in with different amounts) items: $ref: '#/components/schemas/SevenCardStudSidePot' dealerIdx: type: integer description: Index of the dealer for this round example: 0 currentTurn: type: integer description: Index of the player whose turn it is example: 0 phase: type: integer description: | Current game phase: - `0` — Init - `1` — Third Street - `2` — Fourth Street - `3` — Fifth Street - `4` — Sixth Street - `5` — Seventh Street - `6` — Showdown - `7` — End - `8` — Rebuy/Addon enum: [0, 1, 2, 3, 4, 5, 6, 7, 8] example: 1 gameEndFlag: type: boolean description: Whether the round has ended example: false lastBet: type: integer description: The current highest bet in this betting round minimum: 0 example: 10 minRaise: type: integer description: Minimum raise amount minimum: 0 example: 20 bettingLimit: type: integer description: | Betting limit type: 0 = Fixed, 1 = Pot Limit, 2 = No Limit. enum: [0, 1, 2] raiseCount: type: integer description: Number of raises in the current betting round. minimum: 0 maxBetAmount: type: integer description: | Maximum allowed bet/raise amount for Pot Limit mode. 0 means no limit (Fixed or No Limit mode). minimum: 0 roundResults: type: array description: Results for each non-folded player at showdown items: $ref: '#/components/schemas/SevenCardStudResult' cpuActions: type: array description: CPU actions that occurred since the last human action items: $ref: '#/components/schemas/SevenCardStudCpuAction' message: type: string description: | Game result or status message. example: "" messageCode: type: string description: Machine-readable message code corresponding to the message field. messageParams: type: object additionalProperties: type: string description: Interpolation parameters for the message template keyed by placeholder name. handCount: type: integer description: Number of hands played (for tournament mode display) minimum: 0 example: 0 ante: type: integer description: Current ante amount minimum: 1 example: 2 bringIn: type: integer description: Current bring-in amount minimum: 1 example: 5 smallBet: type: integer description: Small bet amount (Third/Fourth Street) minimum: 1 example: 10 bigBet: type: integer description: Big bet amount (Fifth/Sixth/Seventh Street) minimum: 1 example: 20 tournamentMode: type: boolean description: Whether tournament mode (ante escalation) is active example: false anteLevelHands: type: integer description: Number of hands before ante increases minimum: 1 example: 10 anteMultiplier: type: integer description: Ante multiplier percentage (e.g. 200 = 2x) minimum: 101 example: 200 tableSize: type: integer description: Current table size (2-7) minimum: 2 maximum: 7 example: 4 bringInPlayerIdx: type: integer description: Index of the player required to post the bring-in (lowest door card) example: 2 rebuyAvailable: type: boolean description: Whether rebuy is available in the current phase example: false addonAvailable: type: boolean description: Whether addon is available in the current phase example: false rebuyPhaseType: type: integer description: | Type of rebuy phase: 1 = rebuy, 2 = addon. Only present when phase is 8 (Rebuy/Addon). enum: [1, 2] rebuyChips: type: integer description: Chips available for current rebuy minimum: 0 rebuyMaxCount: type: integer description: Maximum rebuy count setting minimum: 0 rebuyCounts: type: array description: Rebuy count per player (same order as players array) items: type: integer minimum: 0 addonChips: type: integer description: Chips available for addon minimum: 0 muckAvailable: type: boolean description: Whether the human player can muck (hide) their hand at showdown package main import ( "fmt" "os" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/web" ) ⋮---- "fmt" "os" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/web" ⋮---- func main() func run() int package main import ( "fmt" "io" "os" ) ⋮---- "fmt" "io" "os" ⋮---- // completionSubcommands is the list of all subcommands for shell completion. var completionSubcommands = []string{ "baccarat", "blackjack", "bridge", "canasta", "clocksolitaire", "completion", "crazyeights", "cribbage", "daifugo", "deuceswild", "doubt", "euchre", "freecell", "games", "ginrummy", "gofish", "golf", "hearts", "holdem", "indianpoker", "jokerpoker", "klondike", "memory", "napoleon", "ohhell", "oldmaid", "omaha", "pigtail", "pineapple", "pinochle", "poker", "pyramid", "sevencardstud", "sevens", "shortdeck", "spades", "speed", "spider", "threecard", "tripeaks", "update", "videopoker", "web", } // runCompletion outputs a shell completion script for the given shell name. // Returns 0 on success, 1 on error. func runCompletion(args []string) int ⋮---- var err error ⋮---- // writeInstallHint writes shell-specific installation instructions to w as #-prefixed comments. func writeInstallHint(w io.Writer, shell string) ⋮---- var hint string ⋮---- func writeBashCompletion(w io.Writer) error func writeZshCompletion(w io.Writer) error func writeFishCompletion(w io.Writer) error package main import ( "errors" "flag" "fmt" "os" "strconv" "strings" "golang.org/x/term" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller/cuiutil" "github.com/yuta-yoshinaga/go_trumpcards/internal/color" "github.com/yuta-yoshinaga/go_trumpcards/internal/i18n" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/ui" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/update" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/web" ) ⋮---- "errors" "flag" "fmt" "os" "strconv" "strings" "golang.org/x/term" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller/cuiutil" "github.com/yuta-yoshinaga/go_trumpcards/internal/color" "github.com/yuta-yoshinaga/go_trumpcards/internal/i18n" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/ui" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/update" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/web" ⋮---- // Injected via -ldflags at build time (e.g. by GoReleaser). var ( version = "dev" commit = "none" date = "unknown" ) func main() func run() int ⋮---- // Color control: NO_COLOR env var (https://no-color.org/), --no-color flag, // or non-TTY stdout (pipe/redirect auto-detection). ⋮---- // Language detection: --lang > LANG env > default "ja" ⋮---- // gameDescriptions maps canonical game names to display descriptions. ⋮---- // Commands that parse their own sub-flags; skip the extra-args warning for these. ⋮---- // Resolve game name aliases (e.g., "gin" -> "ginrummy", "7stud" -> "sevencardstud"). ⋮---- // No argument: start interactive multi-game mode (defaults to blackjack). ⋮---- func mapKeys(m map[string]func() int) []string //go:build js && wasm package main import ( "log" "net/http" "github.com/syumai/workers" "github.com/syumai/workers/cloudflare" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/presenter" "github.com/yuta-yoshinaga/go_trumpcards/internal/domain" corsmw "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/cors" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/worker" "github.com/yuta-yoshinaga/go_trumpcards/internal/usecase" ) ⋮---- "log" "net/http" "github.com/syumai/workers" "github.com/syumai/workers/cloudflare" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/presenter" "github.com/yuta-yoshinaga/go_trumpcards/internal/domain" corsmw "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/cors" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/worker" "github.com/yuta-yoshinaga/go_trumpcards/internal/usecase" ⋮---- func main() ⋮---- // BlackJack ⋮---- // Baccarat ⋮---- // Poker ⋮---- // Texas Hold'em ⋮---- // Omaha ⋮---- // Short Deck ⋮---- // Indian Poker ⋮---- // Video Poker ⋮---- // Deuces Wild ⋮---- // Joker Poker ⋮---- // Three Card Poker ⋮---- // Pineapple Poker ⋮---- // Seven Card Stud ⋮---- var handler http.Handler = mux //go:build js && wasm package main import ( "log" "net/http" "github.com/syumai/workers" "github.com/syumai/workers/cloudflare" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/presenter" "github.com/yuta-yoshinaga/go_trumpcards/internal/domain" corsmw "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/cors" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/worker" "github.com/yuta-yoshinaga/go_trumpcards/internal/usecase" ) ⋮---- "log" "net/http" "github.com/syumai/workers" "github.com/syumai/workers/cloudflare" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/presenter" "github.com/yuta-yoshinaga/go_trumpcards/internal/domain" corsmw "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/cors" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/worker" "github.com/yuta-yoshinaga/go_trumpcards/internal/usecase" ⋮---- func main() ⋮---- // Hearts ⋮---- // Spades ⋮---- // Euchre ⋮---- // Napoleon ⋮---- // Old Maid ⋮---- // Doubt ⋮---- // Daifugo ⋮---- // Sevens ⋮---- // Crazy Eights ⋮---- // Oh Hell ⋮---- // Contract Bridge ⋮---- // Speed ⋮---- // Go Fish ⋮---- // Pinochle ⋮---- // Pig's Tail ⋮---- var handler http.Handler = mux //go:build js && wasm package main import ( "log" "net/http" "github.com/syumai/workers" "github.com/syumai/workers/cloudflare" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/presenter" "github.com/yuta-yoshinaga/go_trumpcards/internal/domain" corsmw "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/cors" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/worker" "github.com/yuta-yoshinaga/go_trumpcards/internal/usecase" ) ⋮---- "log" "net/http" "github.com/syumai/workers" "github.com/syumai/workers/cloudflare" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/controller" "github.com/yuta-yoshinaga/go_trumpcards/internal/adapter/presenter" "github.com/yuta-yoshinaga/go_trumpcards/internal/domain" corsmw "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/cors" "github.com/yuta-yoshinaga/go_trumpcards/internal/infrastructure/worker" "github.com/yuta-yoshinaga/go_trumpcards/internal/usecase" ⋮---- func main() ⋮---- // Klondike ⋮---- // FreeCell ⋮---- // Spider ⋮---- // Pyramid ⋮---- // TriPeaks ⋮---- // Memory ⋮---- // Gin Rummy ⋮---- // Cribbage ⋮---- // Canasta ⋮---- // Golf ⋮---- // Clock Solitaire ⋮---- var handler http.Handler = mux # ADR-0001: Clean Architecture adoption ## Status Accepted ## Date 2020-11-29 ## Context トランプゲームのアルゴリズムを実装するGoプロジェクトを開始するにあたり、ビジネスロジックをUI・インフラから分離し、テスタビリティと拡張性を確保する必要があった。ゲームが増えてもコアロジックに影響を与えずにCLI・Web等のUIを追加できる構造が求められた。 ## Decision Clean Architectureを採用し、以下の4層構造とする: 1. **Domain** (`internal/domain/`): コアビジネスロジック(カード、デッキ、ゲームルール) 2. **Use Case** (`internal/usecase/`): アプリケーションビジネスルール(インタラクタ + プレゼンターインターフェース) 3. **Adapter** (`internal/adapter/`): データ変換層(コントローラ + プレゼンター実装) 4. **Infrastructure** (`internal/infrastructure/`): 外部接続(CLI、Webサーバー) 依存方向は外側から内側への一方向のみ(`infrastructure` -> `adapter` -> `usecase` -> `domain`)。 ## Consequences - ゲームロジックがUIから完全に独立し、CUI・Web双方に同じロジックを再利用できる - 各レイヤーが独立してテスト可能(モックによるユニットテスト) - 新しいゲーム追加時は各レイヤーにファイルを追加するだけで済む - レイヤー間のインターフェース定義が必要なため、初期の実装コストは高い - 11ゲームまでスケールした現在も構造が維持されている # ADR-0002: Presenter pattern for output abstraction ## Status Accepted ## Date 2020-11-29 ## Context Clean Architecture においてインタラクタ(ユースケース層)が出力フォーマットを直接扱うと、CUIとWebで別々のインタラクタが必要になる。出力処理をビジネスロジックから分離する仕組みが必要だった。 ## Decision Presenterパターンを採用する: - `internal/usecase/presenter/` にプレゼンターインターフェースを定義 - `internal/adapter/presenter/` にCUI用・Web用の具体実装を配置 - インタラクタにプレゼンターをDIし、出力形式をインタラクタから隠蔽 例: `BlackJackPresenter` インターフェースを `BlackJackCuiPresenter`(テキスト出力)と `BlackJackWebPresenter`(JSON出力)が実装。 ## Consequences - インタラクタのコードを変更せずに新しい出力形式(Web JSON、CUIテキスト等)を追加可能 - テスト時はモックプレゼンター(`*_mock.go`)を注入してI/Oなしでテスト可能 - 各ゲームごとにプレゼンターインターフェース・CUI実装・Web実装の3ファイルが必要 - 11ゲーム × 3ファイル = 33ファイルとなるが、パターンが統一されているため保守負荷は低い # ADR-0003: golang-standards/project-layout directory structure ## Status Accepted ## Date 2026-02-23 ## Context ゲーム数が増加するにつれ、フラットなディレクトリ構造ではファイルの管理が困難になった。Goコミュニティの標準的なプロジェクトレイアウトに合わせることで、新規参加者にとっても理解しやすい構造にする必要があった。 ## Decision `golang-standards/project-layout` に従い、以下のようにリストラクチャリング: - `cmd/` — エントリポイント(CLI、サーバー) - `internal/` — ビジネスロジック全体(Clean Architecture レイヤーで構成) - `api/` — OpenAPI仕様 - `frontend/` — Reactフロントエンド これは破壊的変更(`refactor!`)としてコミットされた。 ## Consequences - Goの標準的なプロジェクト構造に準拠し、可読性向上 - `internal/` によりパッケージの外部公開を防止 - 既存のインポートパスすべてが変更となった(破壊的変更) - 以降のすべてのゲーム追加がこの構造に従っている # ADR-0004: React + Vite + TypeScript frontend ## Status Accepted ## Date 2026-02-19 ## Context レガシーのHTMLページはBootstrapとjQueryで構築されており、コンポーネントの再利用が困難だった。ゲーム数の増加に伴い、モダンなフロントエンドフレームワークへの移行が必要になった。 ## Decision React + Vite + TypeScriptでフロントエンドを再構築する: - Viteによる高速な開発サーバーとビルド - TypeScriptによる型安全性 - 再利用可能なコンポーネント(`CardImage`、`CardBack`、`NavBar`等) - SPAとしてGo Webサーバーから静的ファイルを配信 ## Consequences - コンポーネントベースで再利用性が大幅に向上 - TypeScriptによるコンパイル時の型チェックでバグを早期発見 - Viteにより開発時のHMRが高速 - Node.js/npmが新たな依存として必要(npmは後にbunに移行、ADR-0021参照) - ビルド成果物を `public/` に配置し、GoサーバーからSPAとして配信 # ADR-0005: Stateless REST API with session-based state isolation ## Status Accepted ## Date 2026-02-20 ## Context Web APIの設計において、サーバーサイドのゲーム状態管理の方式を決定する必要があった。初期実装では複数のブラウザクライアントが単一のゲーム状態を共有しており、クライアント間で干渉が発生していた。 ## Decision 1. **ステートレスAPI設計**: 各エンドポイント(`POST //exec`)は `{Cmd, ...state}` のJSONを受け取り、完全なゲーム状態をレスポンスとして返す 2. **セッションベースの状態分離**: 各クライアントが独立したゲームインスタンスを持つ ## Consequences - 各クライアントが独立してゲームをプレイ可能 - リクエストごとに完全な状態を受け取るため、サーバーの状態管理が簡潔 - フロントエンドがゲーム状態を保持し、毎回送信する設計 - API設計が統一的(全ゲーム共通の `POST //exec` パターン) # ADR-0006: OpenAPI specification as API contract ## Status Accepted ## Date 2026-02-21 ## Context エンドポイント数が増加するにつれ、API仕様の正式なドキュメントが必要になった。開発者間でのAPI契約の共有と、ドキュメントの一元管理が求められた。 ## Decision `api/openapi.yaml` をREST APIの唯一の信頼できるソース(Single Source of Truth)とする。エンドポイントやスキーマの変更時は必ずOpenAPI仕様を更新する。 ## Consequences - API仕様が形式的に定義され、フロントエンド開発者との認識齟齬を防止 - ドキュメントメンテナンスルールにより、コード変更とドキュメント更新が同期 - OpenAPI仕様の保守コストが発生するが、11エンドポイントの規模では管理可能 # ADR-0007: Tailwind CSS (replacing Bootstrap) ## Status Accepted ## Date 2026-02-21 ## Context BootstrapはReactコンポーネントパターンとの相性が悪く、不要なCSSが多かった。ユーティリティファーストのCSSフレームワークに移行することで、コンポーネント単位でスタイリングを完結させたかった。 ## Decision BootstrapをTailwind CSSに置換する。レガシーHTMLページとBootstrapの静的アセットをすべて削除。 ## Consequences - ユーティリティクラスによりコンポーネント単位でスタイルが完結 - 未使用CSSがビルド時にパージされ、バンドルサイズ削減 - Bootstrapのグリッドシステムやコンポーネントが使えなくなる - クラス名が長くなる傾向があるが、Reactコンポーネントの再利用で緩和 # ADR-0008: Biome for linting and formatting (replacing ESLint) ## Status Accepted ## Date 2026-02-21 ## Context ESLint + Prettierの2ツール構成は設定の重複や競合が発生しやすかった。より高速でシンプルな単一ツールが求められた。 ## Decision ESLintとPrettierをBiomeに統合。`bun run check` で lint とフォーマットチェックを一括実行。 ## Consequences - 単一ツールでlint + フォーマットを実行でき、設定がシンプル - RustベースのBiomeはESLintより高速 - ESLintの一部ルールがBiomeに未対応の場合がある - `bun run check` コマンドでCI/ローカル双方で同じチェックを実行 # ADR-0009: TDD cycle and 100% branch coverage requirement ## Status Superseded by [ADR-0026](0026-relax-coverage-target.md) ## Date 2026-02-19 ## Context カードゲームはランダム性が高く、バグの検出が困難。確実な品質保証のため、厳格なテスト方針が必要だった。 ## Decision 1. **TDDサイクル(Red-Green-Refactor)の必須化**: すべての実装はテストを先に書く 2. **100%ブランチカバレッジ(C1)**: `cmd/` と `internal/infrastructure/` を除くすべての `internal/` パッケージ、フロントエンドの `api/`・`components/`・`pages/`・`utils/` で必須 3. **4層テスト構造**: Domain、Use Case、Presenter、Controller の各レイヤーでテスト 4. **決定的テスト**: `AddCard` による手動セットアップ、シャッフル順序に依存しない、ランダム分岐はリトライループ(最大1000回)で両分岐をカバー 5. **モックパターン**: `testify/mock` を使用、`*_mock.go` ファイルをインターフェース隣接に配置 ## Consequences - 高い品質保証: 11ゲームすべてで回帰テストが機能 - ランダム性に依存しないテストにより、CIでのフレイキーテストを排除 - テスト作成コストが高いが、バグ修正コストが大幅に削減 - 新しいゲーム追加時のテストテンプレートが確立されている # ADR-0010: TanStack React Query for API state management ## Status Accepted ## Date 2026-03-04 ## Context カスタムの `useGameApi` フックで各ページがローディング・エラー状態を手動管理しており、ボイラープレートが多かった。 ## Decision TanStack React Query(`useMutation`)を採用し、API状態管理を統一する。`QueryClientProvider` でアプリ全体をラップ。 ## Consequences - ローディング・エラー・成功状態の管理が宣言的になり、ボイラープレート削減 - テストでは `renderWithProviders` で `QueryClientProvider` をラップする必要がある - リトライ、キャッシュ等のReact Queryの機能を将来活用可能 # ADR-0011: i18n with react-i18next and browser language detection ## Status Accepted ## Date 2026-03-05 ## Context 日本語のみのUIでは国際的なユーザーが利用しにくい。Web GUI と CLI の両方で多言語対応が必要だった。 ## Decision - **Web GUI**: `react-i18next` + `i18next-browser-languagedetector` で日本語/英語をサポート - **CLI**: `--lang` フラグで言語切替 - **翻訳ファイル**: `frontend/src/i18n/locales/{ja,en}/` にゲームごとに配置 - **サーバーレスポンス**: `messageCode` と `messageParams` を `message` と併せて返し、フロントエンド側で翻訳 ## Consequences - ブラウザの言語設定に基づく自動言語検出 - ゲームごとに翻訳ファイルが分離され、保守しやすい - 新しいゲーム追加時に翻訳ファイル(ja/en)の作成が必要 - サーバーが `messageCode` を返すことで、翻訳ロジックがフロントエンドに集約 # ADR-0012: Playwright for E2E testing ## Status Accepted ## Date 2026-03-04 ## Context ユニットテストだけではフロントエンドとバックエンドの統合動作を検証できない。実際のブラウザでのゲームフロー(ナビゲーション、ボタン操作、フェーズ遷移)を確認する仕組みが必要だった。 ## Decision Playwright(Chromiumのみ)でE2Eテストを実装。`frontend/e2e/` に配置。Go サーバーをポート8080で自動起動してテスト。 ## Consequences - 実際のブラウザでゲームフロー全体を検証可能 - カードのランダム性があるため、特定のカード値ではなくフロー(ボタンの表示/非表示、フェーズ遷移、リセット動作)をアサート - テスト実行にGoサーバーの起動が必要で、実行時間が長い - Chromiumのみに限定することで、ブラウザ互換性テストは省略(トレードオフ) # ADR-0013: Multi-stage Docker build with distroless image ## Status Accepted ## Date 2026-02-20 ## Context 再現可能なデプロイメント環境が必要だった。また、本番イメージのセキュリティと軽量化も求められた。 ## Decision マルチステージDockerビルドを採用: 1. Node.jsステージ: フロントエンドのビルド 2. Goステージ: バックエンドのビルド 3. 最終ステージ: Google distroless イメージ(ベースイメージのダイジェスト固定) ## Consequences - 最終イメージにビルドツールが含まれず、攻撃面が最小化 - ダイジェスト固定により再現可能なビルドを保証 - distroless イメージにはシェルがないため、デバッグ時にはマルチステージの中間イメージを使用する必要がある # ADR-0015: WCAG accessibility compliance ## Status Accepted ## Date 2026-03-11 ## Context Web GUIにキーボードナビゲーションやスクリーンリーダーサポートがなく、アクセシビリティが不十分だった。 ## Decision WCAGガイドラインに準拠したアクセシビリティ改善を体系的に実施: - **セマンティックHTML**: `fieldset`/`legend`、`label`/`htmlFor` の適切な使用 - **キーボードナビゲーション**: 矢印キー、Enter、Escape での操作(全ゲーム対応) - **フォーカス管理**: WCAG 2.4.7 準拠の可視フォーカスインジケーター - **`aria-live` リージョン**: アクションログパネルのリアルタイム更新通知 - **`aria-pressed`**: カード選択状態の伝達 - **色のコントラスト**: WCAG 1.4.1 / 1.4.3 準拠 - **`scope="col"`**: テーブルヘッダーのアクセシビリティ - **キーボードショートカット**: 全ゲームマニュアルにドキュメント化 ## Consequences - キーボードのみでの操作が可能になり、アクセシビリティが大幅に向上 - スクリーンリーダーでゲーム状態を把握可能 - 全ゲームページにアクセシビリティ属性の追加が必要(保守コスト増) - E2Eテストの更新が必要(ダイアログのキーボード操作等) # ADR-0016: Production middleware stack (CORS, security headers) ## Status Accepted ## Date 2026-03-01 ## Context Web APIを本番環境で公開するにあたり、セキュリティヘッダーとCORS設定が必要だった。 ## Decision - **CORS**: `CORS_ALLOWED_ORIGINS` 環境変数で設定可能。本番で未設定の場合はスキップ - **`DefaultProdStack`**: 情報漏洩を防ぐセキュリティヘッダーを付与するミドルウェアスタック ## Consequences - 環境変数ベースの設定により、開発・本番で柔軟にCORS制御可能 - デフォルトで安全な設定(未設定時はCORSなし) - セキュリティヘッダーが自動的に全レスポンスに付与 # ADR-0019: CI/CD pipeline (CodeQL, golangci-lint, auto-tagging) ## Status Accepted ## Date 2021-04-15 ## Context コード品質とセキュリティの自動チェック、およびリリースの自動化が必要だった。 ## Decision GitHub Actionsで以下のパイプラインを構築: - **CodeQL**: push/PR時にセキュリティスキャン - **golangci-lint**: Go コードの静的解析 - **CI**: バックエンド・フロントエンド双方のテスト自動実行(2026-02-19追加) - **Auto-tag**: `master` へのマージ時に自動でgitタグとGitHub Releaseを作成 ## Consequences - セキュリティ脆弱性とコード品質問題を自動検出 - リリースプロセスが自動化され、手動タグ付けが不要 - PRマージ前に全テストが通ることを保証 # ADR-0021: Migrate package manager from npm to bun ## Status Accepted ## Date 2026-03-15 ## Context ローカル開発時にnpm/npxのメモリ使用量が大きく、動作が重くなる問題があった。Docker imageサイズもnode:alpine (~170MB) では大きかった。全フロントエンド依存ツール(Vite, Vitest, Biome, Playwright, Tailwind, TypeScript)がbunと完全互換であることを確認済み。 ## Decision パッケージマネージャーをnpmからbun 1.3.10に移行する: - ロックファイル: `package-lock.json` → `bun.lock`(テキスト形式) - Docker: `node:24-alpine` → `oven/bun:1.3.10-alpine`(ダイジェスト固定) - CI: `actions/setup-node` → `oven-sh/setup-bun@v2`(バージョン固定: 1.3.10) - バージョン固定戦略: 完全なパッチバージョン固定(最大限の再現性) `deploy-repomix.yml` はフロントエンドとは独立しているため、移行対象外とする。 ## Consequences - メモリ使用量の大幅削減(npm比で50-70%削減の報告あり) - インストール速度の向上(npm比で10-25倍高速) - Docker imageサイズの縮小(`oven/bun:alpine` ~60-70MB vs `node:alpine` ~170MB) - bunのバージョンアップは手動で行う必要がある(パッチバージョン固定のため) - `bun test` はbun独自のテストランナーを起動するため、Vitestの実行には `bun run test` を使う必要がある # ADR-0022: Automated quality gates via Claude Code hooks ## Status Accepted ## Date 2026-03-20 ## Context git履歴(1253コミット)を分析した結果、繰り返し発生する修正コミットのパターンが特定された: - **goimportsフォーマット漏れ**: 12回(新規Goファイル作成後に実行忘れ → CI失敗 → 修正コミット) - **golangci-lint / staticcheck警告**: 12回(lint未実行でコミット → CI失敗 → 修正コミット) - **biomeフォーマット / import ordering**: 7回(フロントエンドファイル編集後にcheck忘れ) - **PRレビュー指摘への修正**: 61回(大きなfeatを一度にPR → 多数の指摘 → 修正コミット) - **コード重複 → 後追いリファクタ**: 71回(新ゲーム追加時にコピー&ペースト → 後から共通化) - **E2Eテスト不安定**: 12回(ランダム性依存のアサーション、不適切なタイムアウト) - **デッドコード残留**: 9回 - **カバレッジ不足**: 9回 これらは手動プロセスへの依存が原因であり、自動化で防止可能なものが大半だった。 ## Decision Claude Code hooks(`.claude/settings.json`)を使い、以下の自動品質ゲートを導入する: ### PostToolUse hooks(Write|Edit時に自動実行) 1. **goimports自動実行**: `.go`ファイルをWrite/Editした直後に`goimports -w`を自動実行 2. **biome自動修正**: `.ts`/`.tsx`ファイルをWrite/Editした直後に`bunx biome check --write`を自動実行 ### PreToolUse hooks(git commit前にブロッキングチェック) 3. **golangci-lint**: ステージされたGoファイルがある場合、`golangci-lint run ./...`を実行。警告があればコミットをブロック 4. **biome check**: ステージされたTS/TSXファイルがある場合、`bun run check`を実行。エラーがあればコミットをブロック 5. **ドキュメント乖離検知**: agentフックでステージされたファイルからドキュメント更新漏れを検出。CLAUDE.mdのDocumentation Maintenanceテーブルのルールを適用 ### ドキュメント・ガイドラインの強化 6. **新ゲーム追加チェックリスト**: CLAUDE.mdに24項目のチェックリストを追加。共通ヘルパーの一覧と、バックエンド/フロントエンド/ドキュメントの全ステップを網羅 7. **E2Eテストガイドライン**: frontend/CLAUDE.mdにflaky testを防ぐための具体的ルールを追記 8. **`/doc-drift-check`スキル**: 既存の乖離を一括で見つけるためのスキル(`.claude/skills/doc-drift-check/`) ## Consequences **メリット:** - フォーマット修正コミットの完全排除(goimports 12回、biome 7回 → 0回) - lint警告の事前検出(golangci-lint 12回 → 0回) - ドキュメント更新漏れの自動検出 - 新ゲーム追加時の品質底上げ(チェックリストにより重複コードとPRレビュー指摘を削減) - E2Eテストの安定性向上 **デメリット:** - PostToolUseフックにより毎回のファイル保存で0.5-1秒のオーバーヘッド - PreToolUseのgolangci-lintフックでコミット前に最大2分のブロッキング - agentフック(ドキュメント乖離検知)はLLM呼び出しのためコストが発生 - hookコマンドはツール(`goimports`, `golangci-lint`, `bun`)がPATHに設定されている前提 **リスク軽減:** - フックは`|| true`で非致命的エラーを無視し、通常の開発フローを阻害しない - ブロッキングチェック(PreToolUse)はgit commitコマンドのみに限定し、他のBashコマンドはスルー - `/hooks`メニューからいつでも無効化可能 # ADR-0023: GoDoc/TSDoc + GitHub PagesによるAPIドキュメント自動生成 ## Status Accepted ## Date 2026-03-20 ## Context ソースコードのドキュメントと閲覧可能なAPIリファレンスが分離していた。開発者やAIアシスタントがGoパッケージやTypeScriptモジュールの一元的な自動生成ドキュメントを参照する手段がなかった。16以上のゲーム実装を持つバックエンド・フロントエンドにおいて、手動でのドキュメント保守はスケールしない。 必要だったもの: 1. すべてのエクスポートされたシンボルへのインラインドキュメントコメント(GoはGoDoc、TypeScriptはTSDoc) 2. 閲覧可能なHTMLドキュメントの自動生成 3. リリースごとに公開サイトへの自動デプロイ ## Decision 以下のドキュメントスタックを採用する: - **GoDocコメント**(`// SymbolName description`)を`internal/`配下のすべてのエクスポートされたGoシンボルに付与 - **TSDocコメント**(`/** description */`)を`frontend/src/`配下のすべてのエクスポートされたTypeScriptシンボルに付与 - **gomarkdoc**でGoパッケージからMarkdownを生成し、**pandoc**でHTMLに変換 - **TypeDoc**でTypeScriptソースからHTMLドキュメントを生成 - **GitHub Pages**デプロイをGitHub Actionsで`master`へのpush時に実行(既存のrepomixデプロイと統合) 生成されるサイト構成: ``` _site/ index.html # リンク付きランディングページ repomix-output.txt # 圧縮リポジトリスナップショット go/ # Go APIドキュメント(パッケージごとのHTML) ts/ # TypeScript APIドキュメント(HTML) ``` ## Consequences **メリット:** - すべてのエクスポートされたシンボルにインラインドキュメントが付き、IDEホバー情報とコードの可読性が向上 - 自動生成ドキュメントがコードと同期し続ける(手動でのHTML保守が不要) - 単一のワークフローが以前のrepomix専用デプロイを置き換え - 開発者とAIアシスタントがGitHub PagesのURLでAPIドキュメントを閲覧可能 **デメリット:** - 約400ファイルへのGoDoc/TSDoc追加は大きな初期投資 - CIビルド時間がわずかに増加(gomarkdoc + pandoc + TypeDoc生成) - TypeDocがdev dependencyとして追加され、`node_modules`サイズがわずかに増加 **その他:** - ドキュメント品質はコメント品質に依存する(自動生成は正確性を検証しない) - `deploy-repomix.yml`ワークフローは統合された`deploy-pages.yml`に置き換えられ削除 # ADR-0026: ブランチカバレッジ基準を100%から80%に緩和 ## Status Accepted ## Date 2026-03-23 ## Context ADR-0009 で導入した100%ブランチカバレッジ(C1)基準は、プロジェクト初期の品質確保に大きく貢献した。しかし、ゲーム数が18に増加し、フロントエンド・バックエンド双方でテストコードが肥大化するにつれ、以下の問題が顕著になった: 1. **メンテナンスコスト**: 死コード分岐(ボタン disabled で到達不能な `if` 等)をカバーするためだけに複雑なテストを書く必要があり、実装変更のたびにテストの修正負担が大きい 2. **テスト実行時間**: テストケースの肥大化により、WSL2 の限られたリソース(~2 GB RAM)でのテスト実行時間が長期化 3. **生産性低下**: 特にフロントエンドでは、品質維持・向上効果以上のメンテナンスコストと生産性低下を招いている 4. **ランダム分岐の無理なカバレッジ**: 1000回リトライループなど、100%を達成するためだけの不自然なテストパターンが存在 TDD のベストプラクティスでは、80%のカバレッジが推奨されており、それ以上はコストパフォーマンスが急激に悪化する(収穫逓減)。 ## Decision 1. **ブランチカバレッジ基準を80%に変更**: Go(`cmd/`・`internal/infrastructure/` 除く)、フロントエンド(`api/`・`components/`・`pages/`・`utils/`)ともに80%以上を必須とする 2. **TDDサイクルは維持**: Red-Green-Refactor のワークフローは引き続き必須 3. **テスト方針の簡素化**: 到達不能分岐の無理なカバレッジ、1000回リトライループによるランダム分岐カバレッジは不要とする 4. **ADR-0009 のその他の方針は継続**: 4層テスト構造、決定的テスト(`AddCard` 等)、モックパターンはそのまま維持 ## Consequences - テストのメンテナンスコストが大幅に削減され、新機能の開発速度が向上する - テスト実行時間が短縮され、リソース制約下での開発体験が改善する - 重要なビジネスロジック(ゲームルール、スコアリング等)に集中したテストが書ける - カバレッジ率が下がることで、一部のエッジケースが見逃される可能性はあるが、TDDサイクルの維持とE2Eテストで補完する # ADR-0027: Cloudflare Workers (TinyGo/Wasm) によるエッジデプロイ ## Status Accepted ## Date 2026-03-28 ## Context 既存のデプロイ先である Render(Docker)は無料枠でコンテナがスリープし、初回アクセス時に30〜60秒のコールドスタートが発生していた。ユーザー体験を損なうこの遅延を解消するため、エッジコンピューティング基盤への移行を検討した。 Cloudflare Workers はリクエスト駆動でコールドスタートが事実上なく、無料枠(1日10万リクエスト)で十分な規模をカバーできる。ただし、無料枠のデプロイサイズ上限は **圧縮後1MB** であり、Go標準コンパイラで生成した Wasm バイナリ(26ゲーム全体で約11MB、gzip後約2.9MB)ではこの制限を超過する。 ## Decision 以下の構成を採用した: ### 1. TinyGo による Wasm コンパイル Go 1.26 の代わりに TinyGo 0.40.1(Go 1.25 対応)を使用し、Wasm バイナリサイズを大幅に削減する。`go.mod` に `go 1.25.8` + `toolchain go1.26.0` を指定することで、Docker ビルド(Go 1.26)と Workers ビルド(TinyGo/Go 1.25)の両方を同一リポジトリで維持する。 ### 2. 3-Worker 分割構成 26ゲームをフロントエンドの既存カテゴリに合わせて3つの Worker に分割: | Worker | カテゴリ | ゲーム数 | gzip後サイズ | |--------|---------|---------|-------------| | casino | テーブル + ポーカー | 10 | ~565 KB | | classic | トリックテイキング + マッチング | 9 | ~589 KB | | solo | ソリティア + ラミー | 7 | ~524 KB | 全 Worker が無料枠の1MB制限に収まる。 ### 3. フロントエンドの Worker URL ルーティング `gameApi.ts` にゲームごとの Worker URL マッピングを追加。環境変数 `VITE_WORKER_{CASINO,CLASSIC,SOLO}_URL` が未設定の場合は相対URLにフォールバックし、Docker デプロイとの互換性を維持する。 ### 4. CI/CD パイプライン - `develop` マージ → ステージング環境にデプロイ(Worker名に `-staging` サフィックス) - `master` マージ → 本番環境にデプロイ - サイズチェック: gzip後1MB超で CI 失敗 ### 検討した代替案 | 案 | 不採用理由 | |----|-----------| | 標準Go Wasm(有料枠$5/月) | gzip後2.9MBで無料枠に収まらない。有料枠(5MB)なら可能だが、無料枠を優先した | | TinyGo 単一Worker | 26ゲーム全体だとgzip後1MBを超える可能性が高い | | Fly.io 等のコンテナPaaS | コールドスタート問題が残る | | TinyGo対応を待ってGo 1.26維持 | TinyGoのGo 1.26サポートが未定。`go.mod` のデュアルバージョン戦略で即座に対応可能だった | ## Consequences - **コールドスタート解消**: Workers はリクエスト駆動のため、Docker PaaS のようなスリープ→起動の遅延がない - **go-json-rest 依存の除去**: TinyGo 互換性のために `go-json-rest` を標準 `net/http` に置き換えた(PR #1012)。結果として外部依存が1つ減り、コードも約2,000行削減された - **モック ファイルのビルドタグ**: 全モックファイルに `//go:build test` タグを追加。TinyGo ビルドから `testify/mock` を除外するために必要だった - **Go バージョン制約**: TinyGo が Go 1.25 までしかサポートしないため、`go.mod` に `go 1.25.8` を指定。`toolchain go1.26.0` ディレクティブにより通常開発は Go 1.26 を使用 - **メソッドプレフィックスルーティング不可**: TinyGo の `net/http` は Go 1.22 の `"POST /path"` パターンを未サポート。Workers エントリポイントでは通常の `"/path"` パターンを使用 - **セッション管理**: `SessionProvider[T]` インターフェースにより、Docker 版(インメモリ `MemorySessionProvider`)と Workers 版(`KVSessionProvider` + Cloudflare KV)を統一的に扱う。KV 版はゲームドメインオブジェクトを JSON シリアライズして永続化し、TTL=1時間で自動削除される。KV 無料枠(書き込み 1,000回/日)のためデモ用途に限定。全26ゲーム対応済み(詳細は [ADR-0028](0028-kv-session-persistence.md) を参照) # ADR-0028: Cloudflare KV によるセッション永続化 ## Status Accepted ## Date 2026-03-28 ## Context ADR-0027 で Cloudflare Workers へのエッジデプロイを採用したが、Workers はステートレスのためリクエスト間でゲームセッション(進行状態・設定・チップ等)が保持されない。Docker 版ではインメモリの `SessionStore[T]` がセッションを管理しているが、Workers 版では毎回新規インタラクターが生成され、ゲーム体験が大きく損なわれていた。 ## Decision 以下の構成でセッション永続化を実装した: ### 1. SessionProvider インターフェース `SessionStore[T]` を直接参照していた `execWithSession` を `SessionProvider[T]` インターフェースに抽象化: - **MemorySessionProvider**: 既存の `SessionStore` をラップ(Docker 版、変更なし) - **KVSessionProvider**: Cloudflare KV でセッション状態を読み書き(Workers 版) Docker 版のコードパスは一切影響を受けない。 ### 2. ドメインオブジェクトの JSON シリアライズ 全26ゲームのドメイン型に `MarshalJSON`/`UnmarshalJSON` を実装。JSON フィールド名は短縮形(`"d"`, `"v"`, `"tc"` 等)を使い KV ストレージサイズを最小化。不正入力への防御として全スライスフィールドに `maxSliceLen=1000` のバリデーションを追加。 ### 3. KV 環境分離 | 環境 | KV namespace | 用途 | |------|-------------|------| | prod | `GAME_SESSIONS` | 本番セッション | | staging | `GAME_SESSIONS_STAGING` | ステージング | | local | `GAME_SESSIONS_preview` | ローカル開発 | wrangler.toml の `[env.staging]` セクションで staging/prod の KV を分離。 ### 検討した代替案 | 案 | 不採用理由 | |----|-----------| | コマンドリプレイ(イベントソーシング) | シャッフルの非決定性により同じゲーム状態を再現できない | | ドメインフィールドの export | 全26ゲームの構造体フィールドを公開する必要があり、カプセル化を破壊する | | Durable Objects | 無料枠の制約が KV より厳しく、デモ用途には過剰 | ## Consequences - **セッション永続化**: Workers 版でも Docker 版と同等のゲーム体験が実現 - **KV 無料枠制約**: 書き込み 1,000回/日のためデモ用途に限定(1アクション=1書き込み) - **Undo 非永続**: ソリティア系ゲームの Undo 履歴(snapshot)は KV に保存しない。復元後は Undo 不可 - **CPU 記憶非永続**: CPU プレイヤーの `memoryManager` は復元時にリセット。CPU は再度学習する - **コード量増加**: 全26ゲームに MarshalJSON/UnmarshalJSON を追加(約5,000行)。パターンは統一されており保守性は維持 # ADR-0029: デザインシステム (DESIGN.md) の導入 ## Status Accepted ## Date 2026-04-04 ## Context Web GUIは36種類のカードゲームを提供しているが、統一的なビジュアルアイデンティティが定義されていなかった。現状はシステムフォント(ui-sans-serif)、Tailwindデフォルトカラー、ゲームごとに場当たり的に選ばれた色を使用しており、競合のカードゲームサイト(solitaire.org、PokerStars、247games等)と視覚的に差別化できていなかった。 デザインシステムの導入にあたり、以下の代替案を検討した: 1. **カジノ風(従来路線の洗練)** — 緑/青のアクセント、サンセリフ体のみ。競合と同質化するリスクが高い。 2. **ミニマリスト/ブルータリスト** — システムフォント、装飾なし。カードゲームの「楽しさ」が伝わりにくい。 3. **ラグジュアリー/リファインド(採用)** — セリフ体見出し、ゴールドアクセント、暖色系ダークテーマ。「高級感のあるプライベートカードルーム」の雰囲気で競合と明確に差別化。 ## Decision `DESIGN.md`をリポジトリルートに配置し、以下のデザインシステムを定義する: - **アエステティック**: Luxury/Refined — カジノフロアではなく、落ち着いたプライベートカードルーム - **タイポグラフィ**: Fraunces(見出し用セリフ体)+ DM Sans(本文)+ Noto Sans JP(日本語フォールバック) - **カラー**: ゴールドアクセント(#D4A853)+ 暖色系ダークニュートラル(#0F1419基調)。ダーク/ライト両モード対応 - **スペーシング**: 4px基準、comfortable密度 - **モーション**: 意図的なイージング、既存のカードアニメーション(flipIn、shake等)は維持 - **レイアウト**: グリッド規律型、240pxサイドバー すべてのビジュアル/UI変更は`DESIGN.md`を参照してから行い、逸脱にはユーザーの明示的な承認を必要とする(`CLAUDE.md`に記載済み)。 ## Consequences ### ポジティブ - カードゲームサイトとして唯一のセリフ体見出し(Fraunces)により、即座に「作り込まれた」印象を与える - ゴールドアクセントで競合の青/緑一辺倒と差別化し、「プレミアム」な印象を無料サービスで実現 - 暖色系テキスト(#E8E0D4)により長時間プレイ時の目の疲労を軽減(AAA準拠 12.8:1) - デザイントークンの一元管理により、36ゲーム間でのビジュアル一貫性を担保 ### ネガティブ - Google Fontsからの外部フォント読み込みが必要(preconnectで緩和) - セリフ体+CJKフォントのフォールバック組み合わせに注意が必要 - 既存のTailwindカラー設定をDESIGN.mdのトークンに段階的に移行する作業が発生 - ライトモードのアクセントカラー(#B8892E)はコントラスト比がAA止まり(4.6:1) # Architecture Decision Records (ADR) This directory contains Architecture Decision Records for the go_trumpcards project. ## Format Each ADR follows the format: - **Status**: Accepted / Superseded / Deprecated - **Context**: What problem or situation prompted this decision? - **Decision**: What was decided? - **Consequences**: What are the trade-offs and implications? **記述言語: 日本語**(タイトル `# ADR-NNNN:` のみ英語可)。新規ADR追加時は本インデックスも更新すること。 **ADR作成基準**: 以下の3つ全てに該当する場合のみADRを作成する(詳細は [`CLAUDE.md`](../../CLAUDE.md) の「ADR記録のリトマステスト」を参照): 1. 他の選択肢を真剣に検討した 2. 覆すと複数ファイル/レイヤーの変更が必要 3. 6ヶ月後の新メンバーが「なぜ?」と疑問に思う ADR番号は連番ではない — 欠番はリトマステスト導入時に非アーキテクチャ的と判断され削除されたレコード。 ## Index | ADR | Title | Status | Date | |-----|-------|--------|------| | [ADR-0001](0001-clean-architecture.md) | Clean Architecture adoption | Accepted | 2020-11-29 | | [ADR-0002](0002-presenter-pattern.md) | Presenter pattern for output abstraction | Accepted | 2020-11-29 | | [ADR-0003](0003-golang-standards-layout.md) | golang-standards/project-layout directory structure | Accepted | 2026-02-23 | | [ADR-0004](0004-react-frontend.md) | React + Vite + TypeScript frontend | Accepted | 2026-02-19 | | [ADR-0005](0005-stateless-rest-api.md) | Stateless REST API with session-based state isolation | Accepted | 2026-02-20 | | [ADR-0006](0006-openapi-specification.md) | OpenAPI specification as API contract | Accepted | 2026-02-21 | | [ADR-0007](0007-tailwind-css.md) | Tailwind CSS (replacing Bootstrap) | Accepted | 2026-02-21 | | [ADR-0008](0008-biome-linter.md) | Biome for linting and formatting (replacing ESLint) | Accepted | 2026-02-21 | | [ADR-0009](0009-tdd-and-coverage.md) | TDD cycle and 100% branch coverage requirement | Superseded by ADR-0026 | 2026-02-19 | | [ADR-0010](0010-tanstack-react-query.md) | TanStack React Query for API state management | Accepted | 2026-03-04 | | [ADR-0011](0011-i18n-react-i18next.md) | i18n with react-i18next and browser language detection | Accepted | 2026-03-05 | | [ADR-0012](0012-playwright-e2e.md) | Playwright for E2E testing | Accepted | 2026-03-04 | | [ADR-0013](0013-docker-distroless.md) | Multi-stage Docker build with distroless image | Accepted | 2026-02-20 | | [ADR-0015](0015-accessibility-wcag.md) | WCAG accessibility compliance | Accepted | 2026-03-11 | | [ADR-0016](0016-production-middleware.md) | Production middleware stack (CORS, security headers) | Accepted | 2026-03-01 | | [ADR-0019](0019-ci-cd-pipeline.md) | CI/CD pipeline (CodeQL, golangci-lint, auto-tagging) | Accepted | 2021-04-15 | | [ADR-0021](0021-bun-package-manager.md) | Migrate package manager from npm to bun | Accepted | 2026-03-15 | | [ADR-0022](0022-automated-quality-gates.md) | Automated quality gates via Claude Code hooks | Accepted | 2026-03-20 | | [ADR-0023](0023-api-documentation.md) | GoDoc/TSDoc + GitHub PagesによるAPIドキュメント自動生成 | Accepted | 2026-03-20 | | [ADR-0026](0026-relax-coverage-target.md) | ブランチカバレッジ基準を100%から80%に緩和 | Accepted | 2026-03-23 | | [ADR-0027](0027-cloudflare-workers-wasm.md) | Cloudflare Workers (TinyGo/Wasm) によるエッジデプロイ | Accepted | 2026-03-28 | | [ADR-0028](0028-kv-session-persistence.md) | Cloudflare KV によるセッション永続化 | Accepted | 2026-03-28 | | [ADR-0029](0029-design-system.md) | デザインシステム (DESIGN.md) の導入 | Accepted | 2026-04-04 | # バックエンド設計ドキュメント (UML) 本ドキュメントは go_trumpcards バックエンドシステムの設計をMermaid記法で可視化したものです。 ## 目次 - [1. クラス図](#1-クラス図) - [1.1 コアドメイン (カード・プレイヤー)](#11-コアドメイン-カードプレイヤー) - [1.2 ゲームドメイン (全39ゲーム)](#12-ゲームドメイン-全39ゲーム) - [1.3 ユースケース層 (Interactor・Presenter)](#13-ユースケース層-interactorpresenter) - [1.4 アダプタ層 (Controller・Presenter実装)](#14-アダプタ層-controllerpresenter実装) - [1.5 インフラストラクチャ層](#15-インフラストラクチャ層) - [2. シーケンス図](#2-シーケンス図) - [2.1 CUIゲーム実行フロー](#21-cuiゲーム実行フロー) - [2.2 Web APIゲーム実行フロー](#22-web-apiゲーム実行フロー) - [2.3 セッション管理フロー](#23-セッション管理フロー) - [2.4 VideoPoker ベット・ホールドフロー](#24-videopoker-ベットホールドフロー) - [2.5 ThreeCard ベット・プレイフロー](#25-threecard-ベットプレイフロー) - [2.6 OhHell ビッド・トリックフロー](#26-ohhell-ビッドトリックフロー) - [2.7 Bridge オークション・トリックフロー](#27-bridge-オークショントリックフロー) - [2.8 Pineapple ディスカードフロー](#28-pineapple-ディスカードフロー) - [2.9 Speed プレイフロー](#29-speed-プレイフロー) - [2.10 GoFish 要求フロー](#210-gofish-要求フロ���) - [2.11 PigsTail ドローフロー](#211-pigstail-ドローフロー) - [2.12 SevenCardStud ベッティングフロー](#212-sevencardstud-ベッティングフロー) - [3. ステートマシン図](#3-ステートマシン図) - [3.1 BlackJack フェーズ遷移](#31-blackjack-フェーズ遷移) - [3.2 Poker フェーズ遷移](#32-poker-フェーズ遷移) - [3.3 Texas Hold'em フェーズ遷移](#33-texas-holdem-フェーズ遷移) - [3.4 Hearts フェーズ遷移](#34-hearts-フェーズ遷移) - [3.5 Spades フェーズ遷移](#35-spades-フェーズ遷移) - [3.6 Doubt フェーズ遷移](#36-doubt-フェーズ遷移) - [3.7 Memory フェーズ遷移](#37-memory-フェーズ遷移) - [3.8 Klondike / FreeCell / Spider / Pyramid / TriPeaks / Golf / ClockSolitaire フェーズ遷移](#38-klondike--freecell--spider--pyramid--tripeaks--golf--clocksolitaire-フェーズ遷移) - [3.9 CrazyEights フェーズ遷移](#39-crazyeights-フェーズ遷移) - [3.10 GinRummy フェーズ遷移](#310-ginrummy-フェーズ遷移) - [3.11 Baccarat フェーズ遷移](#311-baccarat-フェーズ遷移) - [3.12 Napoleon フェーズ遷移](#312-napoleon-フェーズ遷移) - [3.13 IndianPoker フェーズ遷移](#313-indianpoker-フェーズ遷移) - [3.14 VideoPoker フェーズ遷移](#314-videopoker-フェーズ遷移) - [3.15 Euchre フェーズ遷移](#315-euchre-フェーズ遷移) - [3.16 Cribbage フェーズ遷移](#316-cribbage-フェーズ遷移) - [3.17 ShortDeck フェーズ遷移](#317-shortdeck-フェーズ遷移) - [3.18 ThreeCard フェーズ遷移](#318-threecard-フェーズ遷移) - [3.19 OhHell フェーズ遷移](#319-ohhell-フェーズ遷移) - [3.20 Bridge フェーズ遷移](#320-bridge-フェーズ遷移) - [3.21 Pineapple フェーズ遷移](#321-pineapple-フェーズ遷移) - [3.22 Speed フェーズ遷移](#322-speed-フェーズ遷移) - [3.23 GoFish フェーズ遷移](#323-gofish-フェーズ遷移) - [3.24 Canasta フェーズ遷移](#324-canasta-フェーズ遷移) - [3.25 Pinochle フェーズ遷移](#325-pinochle-フェーズ遷移) - [3.26 PigsTail フェーズ遷移](#326-pigstail-フェーズ遷移) - [3.27 SevenCardStud フェーズ遷移](#327-sevencardstud-フェーズ遷移) --- ## 1. クラス図 ### 1.1 コアドメイン (カード・プレイヤー) ```mermaid classDiagram class Card { -int design -int value -bool draw } class TrumpCards { -cards []*Card -drawIdx int +NewTrumpCards(jokerCount int) *TrumpCards +Shuffle() +DrawOne() *Card +Remaining() int +Cards() []*Card } class Player { -cards []*Card +AddCard(card *Card) +RemoveCard(index int) *Card +Cards() []*Card +CardLen() int +Shuffle() +Reorder(indices []int) } class GamePlayer { +bool IsHuman +bool IsFinished } class RankedGamePlayer { +int Rank } class ChipHolder { +int Chips +int Bet +AddChips(amount int) +PlaceBet(amount int) error +ResetBet() } class ActionLogEntry { +int TurnNumber +int PlayerIdx +string ActionType +string Detail +[]*Card Cards } TrumpCards --> "*" Card : contains Player --> "*" Card : holds GamePlayer --|> Player : extends RankedGamePlayer --|> GamePlayer : extends GamePlayer *-- ChipHolder : mixin ``` ### 1.2 ゲームドメイン (全39ゲーム) #### ベッティング系ゲーム ```mermaid classDiagram class BlackJack { -trumpCards *TrumpCards -players []*BlackJackPlayer -config BlackJackConfig -phase int +Reset() +PlayerBet(amount int) error +PlayerHit() error +PlayerStand() error +PlayerDoubleDown() error +PlayerSplit() error +PlayerInsurance(accept bool) error +PlayerSurrender() error +Phase() int +ActionLog() []*ActionLogEntry } class BlackJackPlayer { -hands []*BlackJackHand -cpuSeat *BlackJackCpuSeat } class BlackJackHand { +[]*Card Cards +int Bet +bool IsStand +bool IsBusted } class BlackJackConfig { +int DeckCount +bool AllowDoubleDown +bool AllowSplit +bool AllowInsurance +bool AllowSurrender +bool AllowEarlySurrender } class Poker { -trumpCards *TrumpCards -players []*PokerPlayer -config PokerConfig -dealerIdx int -humanProfile *BettingHumanProfile -round pokerRoundState +Reset() +PlayerExchange(indices []int) error +PlayerFold() error +PlayerCheck() error +PlayerCall() error +PlayerBet(amount int) error +PlayerRaise(amount int) error +PlayerAllIn() error +Phase() int } class Baccarat { -trumpCards *TrumpCards -playerHand []*Card -bankerHand []*Card -phase int +Reset() +PlayerBet(betType string, amount int) error +Phase() int } class VideoPoker { -trumpCards *TrumpCards -hand []*Card -heldIndices []int -phase int -betAmount int -handRank int -handName string -payout int -variantConfig *VideoPokerVariantConfig +Reset() +PlayerBet(amount int) error +PlayerHold(indices []int) error +Phase() int +ActionLog() []*ActionLogEntry } class VideoPokerVariantConfig { +string Name +int DeckSize +bool UseJoker +func IsWild func(*Card) bool +func EvalHand func([]*Card) (int, string) +func PayTable func(int, int) int +func MinQualifying func(int) bool } BlackJack --> "*" BlackJackPlayer BlackJack --> "1" BlackJackConfig BlackJackPlayer --|> GamePlayer BlackJackPlayer --> "*" BlackJackHand BlackJackPlayer --> "1" ChipHolder Poker --> "*" PokerPlayer class ThreeCard { -trumpCards *TrumpCards -playerHand []*Card -dealerHand []*Card -phase int -anteBet int -pairPlusBet int -playBet int +Reset() +PlayerBet(amount int, pairPlusBet int) error +PlayerPlay() error +PlayerFold() error +Phase() int +ActionLog() []*ActionLogEntry } Baccarat --> "1" TrumpCards ThreeCard --> "1" TrumpCards ThreeCard --> "1" ChipHolder VideoPoker --> "1" TrumpCards VideoPoker --> "1" ChipHolder VideoPoker --> "0..1" VideoPokerVariantConfig note for VideoPokerVariantConfig "DeucesWild・JokerPoker は独立したドメインクラスを持たず\nVideoPokerVariantConfig のファクトリ関数\n(DeucesWildConfig / JokerPokerConfig) として実装" ``` #### トリックテイキング系ゲーム ```mermaid classDiagram class Hearts { -trumpCards *TrumpCards -players []*HeartsPlayer -config HeartsConfig -phase HeartsPhase -trickCards []*HeartsTrickCard +Reset() +PassCards(indices []int) error +PlayCard(index int) error +NextTrick() error +NextRound() error +Hint() *HeartsHint +Phase() HeartsPhase } class Spades { -trumpCards *TrumpCards -players []*SpadesPlayer -config SpadesConfig -phase SpadesPhase -trickCards []*SpadesTrickCard +Reset() +Bid(amount int) error +PlayCard(index int) error +NextTrick() error +NextRound() error +Hint() *SpadesHint +Phase() SpadesPhase } class HeartsTrickCard { +int PlayerIdx +*Card Card } class SpadesTrickCard { +int PlayerIdx +*Card Card } class Napoleon { -trumpCards *TrumpCards -players []*NapoleonPlayer -config NapoleonConfig -round napoleonRoundState +Reset() +Bid(amount int) error +DeclareTrump(suit int) error +Exchange(indices []int) error +PlayCard(index int) error +NextTrick() error +NextRound() error +Hint() *NapoleonHint +Phase() NapoleonPhase } class NapoleonTrickCard { +int PlayerIdx +*Card Card } class Euchre { -trumpCards *TrumpCards -players []*EuchrePlayer -config EuchreConfig -phase EuchrePhase -trickCards []*EuchreTrickCard -turnedUpCard *Card -trumpSuit int -dealerIdx int +Reset() +OrderUp(alone bool) error +Pass() error +CallTrump(suit int, alone bool) error +Discard(index int) error +PlayCard(index int) error +NextTrick() error +NextRound() error +Hint() *EuchreHint +Phase() EuchrePhase } class EuchreTrickCard { +int PlayerIdx +*Card Card } class OhHell { -trumpCards *TrumpCards -players []*OhHellPlayer -config OhHellConfig -phase OhHellPhase -trickCards []*OhHellTrickCard -trumpCard *Card -trumpSuit int -handSize int +Reset() +PlayerBid(bid int) error +PlayerPlay(cardIndex int) error +NextTrick() +NextRound() +ScoreRound() +GetHint() *OhHellHint +GetPhase() OhHellPhase } class OhHellTrickCard { +int PlayerIdx +*Card Card } class Bridge { -trumpCards *TrumpCards -players []*BridgePlayer -config BridgeConfig -phase BridgePhase -trickCards []*BridgeTrickCard -trumpSuit int -contractLevel int -contractSuit int -declarerIdx int -dummyIdx int -vulnerability [2]bool +Reset() +Bid(bidType int, level int, suit int) error +PlayCard(index int) error +NextTrick() error +NextRound() error +Hint() *BridgeHint +Phase() BridgePhase } class BridgeTrickCard { +int PlayerIdx +*Card Card } Hearts --> "4" HeartsPlayer Hearts --> "*" HeartsTrickCard Spades --> "4" SpadesPlayer Spades --> "*" SpadesTrickCard Napoleon --> "4" NapoleonPlayer Napoleon --> "*" NapoleonTrickCard Euchre --> "4" EuchrePlayer Euchre --> "*" EuchreTrickCard OhHell --> "4" OhHellPlayer OhHell --> "*" OhHellTrickCard Bridge --> "4" BridgePlayer Bridge --> "*" BridgeTrickCard HeartsPlayer --|> GamePlayer SpadesPlayer --|> GamePlayer NapoleonPlayer --|> GamePlayer EuchrePlayer --|> GamePlayer OhHellPlayer --|> GamePlayer BridgePlayer --|> GamePlayer ``` #### ホールデム系ゲーム ```mermaid classDiagram class Holdem { -trumpCards *TrumpCards -players []*HoldemPlayer -config HoldemConfig -communityCards []*Card -phase int -pot int -bettingState BettingState +Reset() +PlayerFold() error +PlayerCheck() error +PlayerCall() error +PlayerBet(amount int) error +PlayerRaise(amount int) error +PlayerAllIn() error +PlayerRebuy() error +PlayerAddon() error +Phase() int } class Omaha { -trumpCards *TrumpCards -players []*OmahaPlayer -config HoldemConfig -communityCards []*Card -phase int +Reset() +PlayerFold() error +PlayerCheck() error +PlayerCall() error +PlayerBet(amount int) error +PlayerRaise(amount int) error +PlayerAllIn() error +Phase() int } class HoldemPlayer { +[]*Card HoleCards +int HandRank +string HandName } class BettingState { +int CurrentBet +[]*SidePot SidePots } class SidePot { +int Amount +[]int EligiblePlayers } Holdem --> "*" HoldemPlayer Holdem --> "1" BettingState BettingState --> "*" SidePot HoldemPlayer --|> GamePlayer HoldemPlayer --> "1" ChipHolder Omaha --> "*" OmahaPlayer OmahaPlayer --|> GamePlayer class ShortDeck { -trumpCards *TrumpCards -players []*ShortDeckPlayer -config HoldemConfig -communityCards []*Card -phase int +Reset() +PlayerFold() error +PlayerCheck() error +PlayerCall() error +PlayerBet(amount int) error +PlayerRaise(amount int) error +PlayerAllIn() error +Phase() int } ShortDeck --> "*" ShortDeckPlayer ShortDeckPlayer --|> GamePlayer class Pineapple { -trumpCards *TrumpCards -players []*PineapplePlayer -config PineappleConfig -communityCards []*Card -phase int -isDiscardPhase bool -discardDone bool +Reset() +PlayerFold() error +PlayerCheck() error +PlayerCall() error +PlayerBet(amount int) error +PlayerRaise(amount int) error +PlayerAllIn() error +PlayerDiscard(cardIdx int) error +PlayerRebuy() error +PlayerAddon() error +Phase() int } Pineapple --> "*" HoldemPlayer Pineapple --> "1" BettingState ``` #### インディアンポーカー ```mermaid classDiagram class IndianPoker { -trumpCards *TrumpCards -players []*IndianPokerPlayer -config IndianPokerConfig -pot int -sidePots []SidePot -dealerIdx int -currentTurn int -phase int -lastBet int -minRaise int -raiseCount int -humanProfile *IndianPokerHumanProfile +Reset() +ResetWithConfig(config IndianPokerConfig) +Action(action int, amount int, humanPlayMs int) error +Phase() int } class IndianPokerPlayer { -isHuman bool -playStyle HoldemPlayStyle +GetIsHuman() bool +GetPlayStyle() HoldemPlayStyle +GetComparisonCards() []*Card } class IndianPokerConfig { +int Ante +int InitChips +BettingLimitType BettingLimit +bool CpuMetaAI +Validate() error } class IndianPokerHumanProfile { +[3]struct AggressiveByBracket +int FoldToBetCount +int FoldToBetTotal +int GamesPlayed +int HesitationCount +float64 HesitationMean +float64 HesitationM2 +BluffRate(bracket int) float64 +FoldRate() float64 } IndianPoker --> "4" IndianPokerPlayer IndianPoker --> "1" IndianPokerConfig IndianPoker --> "*" SidePot IndianPoker --> "1" IndianPokerHumanProfile IndianPokerPlayer --|> Player IndianPokerPlayer --> "1" ChipHolder ``` #### 手札系ゲーム ```mermaid classDiagram class OldMaid { -trumpCards *TrumpCards -players []*OldMaidPlayer -config OldMaidConfig -currentTurn int +Reset() +Draw(index int) error +Phase() int } class Daifugo { -trumpCards *TrumpCards -players []*DaifugoPlayer -config DaifugoConfig -sortMode DaifugoSortMode -round daifugoRoundState +Reset() +Play(indices []int) error +Pass() error +Sort(mode int) +Phase() int } class Sevens { -trumpCards *TrumpCards -players []*SevensPlayer -config SevensConfig -currentTurn int -tablePlaced [5]uint16 +Reset() +Play(cardIdx int) error +PlayJoker(suit int, value int) error +Phase() int } class Doubt { -trumpCards *TrumpCards -players []*DoubtPlayer -config DoubtConfig -phase DoubtPhase -currentTurn int -tableCards []*Card +Reset() +Play(indices []int) error +Doubt() *DoubtDoubtResult +SkipDoubt() +Phase() DoubtPhase } class CrazyEights { -trumpCards *TrumpCards -players []*CrazyEightsPlayer -config CrazyEightsConfig -phase CrazyEightsPhase -discardPile []*Card +Reset() +Play(index int) error +Draw() error +ChooseSuit(suit int) error +NextRound() error +Phase() CrazyEightsPhase } class GinRummy { -trumpCards *TrumpCards -players []*GinRummyPlayer -config GinRummyConfig -phase GinRummyPhase -discardPile []*Card +Reset() +DrawFromStock() error +DrawFromDiscard() error +Discard(index int) error +Knock() error +Layoff(indices []int) error +NextRound() error +Phase() GinRummyPhase } class Speed { -trumpCards *TrumpCards -players []*SpeedPlayer -config SpeedConfig -centerPiles [2]*Card -phase SpeedPhase +Reset() +Play(cardIndex int, pileIndex int) error +Flip() error +Hint() *SpeedHint +Phase() SpeedPhase +ActionLog() []*ActionLogEntry } class SpeedPlayer { -hand []*Card -drawPile []*Card } class SpeedConfig { +int HandSize } OldMaid --> "*" OldMaidPlayer Daifugo --> "*" DaifugoPlayer Sevens --> "*" SevensPlayer Doubt --> "*" DoubtPlayer CrazyEights --> "*" CrazyEightsPlayer GinRummy --> "2" GinRummyPlayer Speed --> "2" SpeedPlayer Speed --> "1" SpeedConfig class GoFish { -trumpCards *TrumpCards -players []*GoFishPlayer -config GoFishConfig -phase GoFishPhase -currentTurn int +Reset() +Ask(targetIdx int, rank int) error +Phase() GoFishPhase +ActionLog() []*ActionLogEntry } class GoFishPlayer { -books [][]*Card +GetBooks() [][]*Card +GetBookCount() int +HasRank(rank int) bool } class GoFishConfig { +GoFishCpuDifficulty CpuDifficulty +bool CpuMetaAI } GoFish --> "4" GoFishPlayer GoFish --> "1" GoFishConfig class Canasta { -trumpCards *TrumpCards -players []*CanastaPlayer -config CanastaConfig -phase CanastaPhase -discardPile []*Card -isFrozen bool +Reset() +DrawFromStock() error +DrawFromDiscard(pairIndices []int) error +Meld(groups [][]int) error +SkipMeld() error +Discard(index int) error +GoOut() error +NextRound() +Phase() CanastaPhase } class CanastaPlayer { -melds []*CanastaMeld -red3s []*Card -hasInitMeld bool } class CanastaMeld { +Cards []*Card +IsNatural bool } Canasta --> "2" CanastaPlayer CanastaPlayer --> "*" CanastaMeld CanastaPlayer --|> GamePlayer class Pinochle { -trumpCards *TrumpCards -players []*PinochlePlayer -config PinochleConfig -phase PinochlePhase -trumpSuit int -highestBid int -highestBidder int -playerMelds [4][]*PinochleMeld +Reset() +PlayerBid(amount int) error +PlayerPass() error +CpuBid() +PlayerCallTrump(suit int) error +CpuCallTrump() +ConfirmMelds() +PlayerPlay(cardIndex int) error +CpuPlay() +ResolveTrick() +NextTrick() +NextRound() } class PinochlePlayer { -team int -bid int -hasPassed bool -meldScore int -trickPoints int } Pinochle --> "4" PinochlePlayer PinochlePlayer --|> GamePlayer OldMaidPlayer --|> GamePlayer DaifugoPlayer --|> RankedGamePlayer SevensPlayer --|> RankedGamePlayer DoubtPlayer --|> GamePlayer CrazyEightsPlayer --|> GamePlayer GinRummyPlayer --|> GamePlayer SpeedPlayer --|> Player GoFishPlayer --|> GamePlayer class PigsTail { -trumpCards *TrumpCards -players []*PigsTailPlayer -center []*Card -currentTurn int -gameEndFlag bool +Reset() +Draw() error +GetGameEndFlag() bool +ActionLog() []*ActionLogEntry } class PigsTailPlayer { -penaltyCards []*Card } PigsTail --> "4" PigsTailPlayer PigsTailPlayer --|> GamePlayer ``` #### セブンカード・スタッド ```mermaid classDiagram class SevenCardStud { -trumpCards *TrumpCards -players []*SevenCardStudPlayer -config SevenCardStudConfig -phase int -pot int -bettingState BettingState -dealerIdx int -currentTurn int -bringInPlayerIdx int -humanProfile *BettingHumanProfile +Reset() +PlayerFold() error +PlayerCheck() error +PlayerCall() error +PlayerBet(amount int) error +PlayerRaise(amount int) error +PlayerAllIn() error +PlayerRebuy() error +PlayerAddon() error +PlayerMuck() error +PlayerShow() error +Phase() int +ActionLog() []*ActionLogEntry } class SevenCardStudPlayer { +[]*Card HoleCards +[]*Card DoorCards +int HandRank +string HandName } class SevenCardStudConfig { +int Ante +int BringIn +int SmallBet +int BigBet +BettingLimitType BettingLimit +int TableSize +bool TournamentMode +int AnteLevelHands +int AnteMultiplier +bool CpuMetaAI } SevenCardStud --> "*" SevenCardStudPlayer SevenCardStud --> "1" SevenCardStudConfig SevenCardStud --> "1" BettingState SevenCardStudPlayer --|> GamePlayer SevenCardStudPlayer --> "1" ChipHolder ``` #### ソリティア系ゲーム ```mermaid classDiagram class Klondike { -trumpCards *TrumpCards -tableau [7][]*KlondikeTableauCard -foundation [4][]*Card -stock []*Card -waste []*Card -config KlondikeConfig -phase KlondikePhase -history []*klondikeSnapshot +Reset() +Draw() error +Move(from string, fromCol int, fromIdx int, to string, toCol int) error +Hint() *KlondikeHint +Undo() error +UndoN(n int) error +UndoToEscape() int +Autocomplete() error +GiveUp() +Phase() KlondikePhase } class FreeCell { -trumpCards *TrumpCards -tableau [8][]*Card -foundation [4][]*Card -freeCells [4]*Card -phase FreeCellPhase -history []*freeCellSnapshot +Reset() +Move(from string, fromCol int, fromIdx int, to string, toCol int) error +Hint() *FreeCellHint +Undo() error +UndoN(n int) error +UndoToEscape() int +Autocomplete() error +GiveUp() +Phase() FreeCellPhase } class Spider { -trumpCards *TrumpCards -tableau [10][]*SpiderTableauCard -foundation [][]*Card -stock []*Card -config SpiderConfig -phase SpiderPhase -history []*spiderSnapshot +Reset() +Deal() error +Move(fromCol int, fromIdx int, toCol int) error +Hint() *SpiderHint +Undo() error +UndoN(n int) error +UndoToEscape() int +Autocomplete() error +GiveUp() +Phase() SpiderPhase } class Pyramid { -trumpCards *TrumpCards -pyramid [7][]*PyramidCard -stock []*Card -waste []*Card -phase PyramidPhase -history []*pyramidSnapshot +Reset() +Draw() error +RemovePair(row1 int, col1 int, row2 int, col2 int) error +RemoveKing(row int, col int) error +RemoveWithWaste(row int, col int) error +RemoveWasteKing() error +GetHint() *PyramidHint +Undo() error +UndoN(n int) error +UndoToEscape() int +GiveUp() +Phase() PyramidPhase } class TriPeaks { -trumpCards *TrumpCards -tableau [4][]*TriPeaksCard -stock []*Card -waste []*Card -phase TriPeaksPhase -history []*tripeaksSnapshot +Reset() +Draw() error +Remove(row int, col int) error +GetHint() *TriPeaksHint +Undo() error +UndoN(n int) error +UndoToEscape() int +GiveUp() +Phase() TriPeaksPhase } class Golf { -trumpCards *TrumpCards -layout [7][5]*GolfCard -stock []*Card -waste []*Card -phase GolfPhase -history []*golfSnapshot +Reset() +Draw() error +Remove(col int) error +GetHint() *GolfHint +Undo() error +UndoN(n int) error +UndoToEscape() int +GiveUp() +Phase() GolfPhase } class ClockSolitaire { -trumpCards *TrumpCards -piles [13][]*ClockCard -currentPileIdx int -stepCount int -phase ClockSolitairePhase +Reset() +Step() error +AutoPlay() error +Phase() ClockSolitairePhase } class Cribbage { -trumpCards *TrumpCards -players []*CribbagePlayer -config CribbageConfig -phase CribbagePhase -crib []*Card -starter *Card -pegCount int -pegPlayedCards []*Card +Reset() +Discard(indices []int) error +Peg(cardIdx int) error +Go() error +ShowNext() error +NextRound() error +Phase() CribbagePhase } class Memory { -trumpCards *TrumpCards -board []*MemoryBoardCard -players []*MemoryPlayer -config MemoryConfig -phase MemoryPhase +Reset() +Flip(position int) error +Next() error +Phase() MemoryPhase } class KlondikeTableauCard { +*Card Card +bool FaceUp } class SpiderTableauCard { +*Card Card +bool FaceUp } class PyramidCard { +*Card Card +bool Removed } class TriPeaksCard { +*Card Card +bool Removed } class MemoryBoardCard { +*Card Card +bool FaceUp +bool Matched } class ClockCard { +*Card Card +bool FaceUp } Klondike --> "*" KlondikeTableauCard FreeCell --> "*" Card Spider --> "*" SpiderTableauCard Pyramid --> "*" PyramidCard TriPeaks --> "*" TriPeaksCard Cribbage --> "*" CribbagePlayer Memory --> "*" MemoryBoardCard Memory --> "*" MemoryPlayer MemoryPlayer --|> GamePlayer ClockSolitaire --> "*" ClockCard ``` ### 1.3 ユースケース層 (Interactor・Presenter) ```mermaid classDiagram class GamePresenter~G~ { <> +Output(game G, lastErr error) string +ActionLogOutput(game G) string } class BlackJackInteractor { -game BlackJackGame -presenter BlackJackPresenter +Reset() string +Hit() string +Stand() string +Bet(amount int) string +DoubleDown() string +Split() string +Insurance(accept bool) string +Surrender() string +ActionLog() string } class PokerPresenter { <> +Output(game PokerGame, lastErr error) string +ActionLogOutput(game PokerGame) string +OddsOutput(game PokerGame) string } class HeartsPresenter { <> +Output(game HeartsGame, lastErr error) string +ActionLogOutput(game HeartsGame) string +HintOutput(game HeartsGame) string } class KlondikePresenter { <> +Output(game KlondikeGame, lastErr error) string +ActionLogOutput(game KlondikeGame) string +HintOutput(game KlondikeGame) string } BlackJackInteractor ..> GamePresenter : uses BlackJackInteractor ..> BlackJackGame : uses note for GamePresenter "各ゲームの Presenter は\nGamePresenter[G] の型エイリアス\nまたは拡張インターフェース" ``` **Interactor パターン (全39ゲーム共通)** ```mermaid classDiagram class GameInteractorIF { <> +Reset() string +ActionLog() string ゲーム固有アクション() string } class GameInteractor { -game GameInterface -presenter GamePresenter +execAndPresent(fn) string +runAndPresent(fn) string } GameInteractor ..|> GameInteractorIF : implements GameInteractor --> GamePresenter~G~ : uses GameInteractor --> GameInterface : uses note for GameInteractor "execAndPresent: error返却アクション実行後Presenter呼出\nrunAndPresent: void アクション実行後Presenter呼出" ``` ### 1.4 アダプタ層 (Controller・Presenter実装) ```mermaid classDiagram class baseController { +writePresenterResponse(w, responseStr) +writeJsonResponse(w, status, body) } class SessionStore~T~ { -sessions map[string]*sessionEntry~T~ +GetOrCreate(sessionId string, factory func() T) T +Get(sessionId string) T } class sessionEntry~T~ { +value T +mu *sync.Mutex } class GameCuiController { -interactor GameInteractorIF +Exec(input string) string } class GameWebController { -store *SessionStore~GameInteractorIF~ +Handle(w ResponseWriter, r *Request) } class GameCuiPresenter { +Output(game G, lastErr error) string +ActionLogOutput(game G) string } class GameWebPresenter { +Output(game G, lastErr error) string +ActionLogOutput(game G) string } GameWebController --|> baseController : extends GameWebController --> SessionStore : uses SessionStore --> "*" sessionEntry : manages GameCuiController --> GameInteractorIF : uses GameCuiPresenter ..|> GamePresenter : implements GameWebPresenter ..|> GamePresenter : implements note for GameCuiController "39ゲーム × CUI/Web = 78 Controller\nGameCuiController / GameWebController は\n各ゲーム毎に具体的な実装が存在" note for GameCuiPresenter "39ゲーム × CUI/Web = 78 Presenter 実装" ``` ### 1.5 インフラストラクチャ層 ```mermaid classDiagram class TrumpCardsWeb { -blackjack *BlackJackWebController -poker *PokerWebController -oldmaid *OldMaidWebController -daifugo *DaifugoWebController -sevens *SevensWebController -doubt *DoubtWebController -holdem *HoldemWebController -omaha *OmahaWebController -shortdeck *ShortDeckWebController -hearts *HeartsWebController -memory *MemoryWebController -klondike *KlondikeWebController -freecell *FreeCellWebController -baccarat *BaccaratWebController -spades *SpadesWebController -crazyeights *CrazyEightsWebController -ginrummy *GinRummyWebController -spider *SpiderWebController -napoleon *NapoleonWebController -indianpoker *IndianPokerWebController -videopoker *VideoPokerWebController -deuceswild *DeucesWildWebController -jokerpoker *JokerPokerWebController -euchre *EuchreWebController -pyramid *PyramidWebController -tripeaks *TriPeaksWebController -cribbage *CribbageWebController -threecard *ThreeCardWebController -ohhell *OhHellWebController -bridge *BridgeWebController -speed *SpeedWebController -gofish *GoFishWebController -pigtail *PigsTailWebController -sevencardstud *SevenCardStudWebController -clocksolitaire *ClockSolitaireWebController +Exec() } class GameManager { -games map[string]CuiExecer -currentGame string +Exec(cmd string) string +Switch(game string) error } class CuiExecer { <> +Exec(input string) string } class GameCui { -controller GameCuiController -domain GameInterface +Exec(input string) string } TrumpCardsWeb --> "*" GameWebController : holds 39 controllers GameManager --> "*" CuiExecer : holds 39 games GameCui ..|> CuiExecer : implements GameCui --> GameCuiController : delegates ``` --- ## 2. シーケンス図 ### 2.1 CUIゲーム実行フロー ```mermaid sequenceDiagram participant User as ユーザー (Terminal) participant Main as main.go participant GM as GameManager participant Cui as GameCui participant Ctrl as CuiController participant Interactor as Interactor participant Domain as Domain (Game) participant Pres as CuiPresenter User->>Main: go run ./cmd/trumpcards Main->>GM: NewGameManager() Main->>GM: RunInteractiveCuiLoop() loop REPL ループ User->>GM: コマンド入力 (例: "hit") GM->>Cui: Exec("hit") Cui->>Ctrl: Exec("hit") Ctrl->>Ctrl: コマンドパース Ctrl->>Interactor: Hit() Interactor->>Domain: PlayerHit() Domain-->>Interactor: error / nil Interactor->>Pres: Output(game, err) Pres-->>Interactor: 整形済み文字列 Interactor-->>Ctrl: 出力文字列 Ctrl-->>Cui: 出力文字列 Cui-->>GM: 出力文字列 GM-->>User: ターミナル表示 end ``` ### 2.2 Web APIゲーム実行フロー ```mermaid sequenceDiagram participant Client as フロントエンド (React) participant Server as TrumpCardsWeb participant WebCtrl as WebController participant Store as SessionStore participant Interactor as Interactor participant Domain as Domain (Game) participant Pres as WebPresenter Client->>Server: POST /blackjack/exec
{"cmd":"hit","sessionId":"abc123"} Server->>WebCtrl: Handle(w, r) WebCtrl->>WebCtrl: JSONパース (WebInput) WebCtrl->>Store: GetOrCreate("abc123", factory) alt 新規セッション Store->>Store: factory() でInteractor生成 Store-->>WebCtrl: 新規Interactor else 既存セッション Store-->>WebCtrl: 既存Interactor (mutex lock) end WebCtrl->>Interactor: Hit() Interactor->>Domain: PlayerHit() Domain-->>Interactor: error / nil Interactor->>Pres: Output(game, err) Pres-->>Interactor: JSON文字列 Interactor-->>WebCtrl: JSON文字列 WebCtrl->>Store: mutex unlock WebCtrl-->>Client: HTTP 200 JSON レスポンス ``` ### 2.3 セッション管理フロー ```mermaid sequenceDiagram participant C1 as クライアント A participant C2 as クライアント B participant WebCtrl as WebController participant Store as SessionStore C1->>WebCtrl: POST {"sessionId":"sess-1","cmd":"reset"} WebCtrl->>Store: GetOrCreate("sess-1") Store->>Store: sessions["sess-1"] 作成 + mutex lock Note over Store: Interactor A 生成 Store-->>WebCtrl: Interactor A (locked) WebCtrl->>WebCtrl: Reset() 実行 WebCtrl->>Store: mutex unlock WebCtrl-->>C1: レスポンス C2->>WebCtrl: POST {"sessionId":"sess-2","cmd":"reset"} WebCtrl->>Store: GetOrCreate("sess-2") Store->>Store: sessions["sess-2"] 作成 + mutex lock Note over Store: Interactor B 生成 (独立) Store-->>WebCtrl: Interactor B (locked) WebCtrl->>WebCtrl: Reset() 実行 WebCtrl->>Store: mutex unlock WebCtrl-->>C2: レスポンス Note over Store: 各セッションは独立した
ゲーム状態を保持 ``` ### 2.4 VideoPoker ベット・ホールドフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as VideoPokerInteractor participant Domain as VideoPoker participant Eval as evalFiveCardHand participant Pres as Presenter Note over User,Pres: ベットフロー User->>Ctrl: bet 3 Ctrl->>Interactor: Bet(3) Interactor->>Domain: PlayerBet(3) Domain->>Domain: チップ減算 → 5枚配布 → phase=Draw Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 手札5枚表示 Note over User,Pres: ホールドフロー User->>Ctrl: hold 0 2 4 Ctrl->>Interactor: Hold([0,2,4]) Interactor->>Domain: PlayerHold([0,2,4]) Domain->>Domain: 非ホールドカードを交換 Domain->>Eval: evalFiveCardHand(hand) Eval-->>Domain: handRank, handName Domain->>Domain: 配当計算 → phase=Result Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 最終手札・役名・配当表示 ``` ### 2.5 ThreeCard ベット・プレイフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as ThreeCardInteractor participant Domain as ThreeCard participant Eval as evalThreeCardHand participant Pres as Presenter Note over User,Pres: ベットフロー User->>Ctrl: bet 100 50 Ctrl->>Interactor: Bet(100, 50) Interactor->>Domain: PlayerBet(100, 50) Domain->>Domain: チップ減算 → 3枚ずつ配布 → phase=Action Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 手札3枚表示 Note over User,Pres: プレイフロー User->>Ctrl: play Ctrl->>Interactor: Play() Interactor->>Domain: PlayerPlay() Domain->>Domain: プレイベット = アンティ額 Domain->>Eval: evalThreeCardHand(playerHand) Eval-->>Domain: playerRank Domain->>Eval: evalThreeCardHand(dealerHand) Eval-->>Domain: dealerRank Domain->>Domain: ディーラー資格判定 → 勝敗判定 → 配当計算 Domain->>Domain: アンティボーナス・ペアプラス計算 → phase=End Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 両手札・結果・配当表示 ``` ### 2.6 OhHell ビッド・トリックフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as OhHellInteractor participant Domain as OhHell participant Pres as Presenter Note over User,Pres: ビッドフロー User->>Ctrl: bid 2 Ctrl->>Interactor: Bid(2) Interactor->>Domain: PlayerBid(2) Domain->>Domain: ビッド記録 → CPU自動ビッド → phase=Play Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: ビッド完了・プレイ開始表示 Note over User,Pres: トリックプレイフロー User->>Ctrl: play 3 Ctrl->>Interactor: Play(3) Interactor->>Domain: PlayerPlay(3) Domain->>Domain: フォロースート検証 → カード出し → CPU自動プレイ Domain->>Domain: トリック勝者判定 → phase=TrickEnd Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: トリック結果表示 ``` ### 2.7 Bridge オークション・トリックフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as BridgeInteractor participant Domain as Bridge participant Pres as Presenter Note over User,Pres: オークションフロー User->>Ctrl: bid 1 1 5 Ctrl->>Interactor: Bid(1, 1, 5) Interactor->>Domain: Bid(BidTypeNormal, 1, NoTrump) Domain->>Domain: ビッド記録 → CPU自動ビッド → コントラクト確定 → phase=Play Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: オークション完了・ダミー公開・プレイ開始表示 Note over User,Pres: トリックプレイフロー User->>Ctrl: play 3 Ctrl->>Interactor: Play(3) Interactor->>Domain: PlayCard(3) Domain->>Domain: フォロースート検証 → カード出し → CPU/ダミー自動プレイ Domain->>Domain: トリック勝者判定 → phase=TrickEnd Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: トリック結果表示 ``` ### 2.8 Pineapple ディスカードフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as PineappleInteractor participant Domain as Pineapple participant Pres as Presenter Note over User,Pres: フロップベッティング完了 → ディスカードフェーズ User->>Ctrl: discard 1 Ctrl->>Interactor: Discard(1) Interactor->>Domain: PlayerDiscard(1) Domain->>Domain: ホールカード[1]を除去 → CPU自動ディスカード → phase=Turn Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: ディスカード完了・ターンカード公開表示 ``` ### 2.9 Speed プレイフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as SpeedInteractor participant Domain as Speed participant Pres as Presenter Note over User,Pres: プレイフロー User->>Ctrl: play 0 1 Ctrl->>Interactor: Play(0, 1) Interactor->>Domain: Play(0, 1) Domain->>Domain: 手札[0]を場札[1]に出す → 手札補充 → CPU自動プレイ Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 場札・手札更新表示 Note over User,Pres: スタック → フリップフロー User->>Ctrl: flip Ctrl->>Interactor: Flip() Interactor->>Domain: Flip() Domain->>Domain: 山札から場札に新カード配置 → phase=Play Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 新しい場札表示 ``` ### 2.10 GoFish 要求フロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as GoFishInteractor participant Domain as GoFish participant Pres as Presenter Note over User,Pres: 要求フロー User->>Ctrl: ask 1 7 Ctrl->>Interactor: Ask(1, 7) Interactor->>Domain: Ask(1, 7) Domain->>Domain: 相手が持っていればカード移動 → ブックチェック → CPU自動プレイ Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: 要求結果・手札更新表示 Note over User,Pres: Go Fish (相手が持っていない場合) User->>Ctrl: ask 2 13 Ctrl->>Interactor: Ask(2, 13) Interactor->>Domain: Ask(2, 13) Domain->>Domain: 相手が持っていない → 山札から1枚引く → ブックチェック → CPU自動プレイ Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: Go Fish結果・手札更新表示 ``` ### 2.11 PigsTail ドローフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as PigsTailInteractor participant Domain as PigsTail participant Pres as Presenter Note over User,Pres: ドローフロー User->>Ctrl: draw Ctrl->>Interactor: Draw() Interactor->>Domain: Draw() Domain->>Domain: 山札から1枚引く → スート一致判定 → ペナルティ or 場に置く → CPU自動プレイ Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: ドロー結果・手札枚数更新表示 ``` ### 2.12 SevenCardStud ベッティングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Ctrl as Controller participant Interactor as SevenCardStudInteractor participant Domain as SevenCardStud participant Pres as Presenter Note over User,Pres: ベッティングフロー (サード~セブンスストリート) User->>Ctrl: bet 40 / call / raise 80 / fold / allin Ctrl->>Interactor: PlayerBet(40) / PlayerCall() / PlayerRaise(80) / PlayerFold() / PlayerAllIn() Interactor->>Domain: アクション実行 Domain->>Domain: ベット処理 → CPU自動アクション → ストリート進行判定 Domain-->>Interactor: nil Interactor->>Pres: Output(game, nil) Pres-->>User: ベッティング結果・カード更新表示 ``` --- ## 3. ステートマシン図 ### 3.1 BlackJack フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bet : Reset() Bet --> Deal : PlayerBet() Deal --> EarlySurrender : ディーラーAce &
EarlySurrender有効 EarlySurrender --> Bet : Surrender (次ラウンド) EarlySurrender --> Insurance : 続行 Deal --> Insurance : ディーラーAce &
Insurance有効 Deal --> Action : ディーラーAce以外 Insurance --> Action : Insurance判定後 Action --> Action : Hit / DoubleDown / Split Action --> End : Stand / Bust / BlackJack End --> Bet : 次ラウンド (Reset) End --> [*] : チップ0 (ゲーム終了) ``` ### 3.2 Poker フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Init : Reset() Init --> Deal : カード配布 + 第1ベッティング Deal --> Exchange : プレイヤーのベット完了 Exchange --> SecondBet : カード交換完了 SecondBet --> End : 第2ベッティング完了 / Fold End --> Init : 次ラウンド (Reset) End --> [*] : チップ0 (ゲーム終了) ``` ### 3.3 Texas Hold'em フェーズ遷移 Omaha Hold'em および Short Deck Hold'em も同一のフェーズ遷移を共有します。 ```mermaid stateDiagram-v2 [*] --> Init : Reset() Init --> PreFlop : ブラインド + ホールカード配布 PreFlop --> Flop : ベッティング完了 PreFlop --> Showdown : 1人以外 Fold Flop --> Turn : ベッティング完了 Flop --> Showdown : 1人以外 Fold Turn --> River : ベッティング完了 Turn --> Showdown : 1人以外 Fold River --> Showdown : ベッティング完了 Showdown --> End : 勝者決定 End --> Rebuy : リバイ/アドオン有効 End --> Init : 次ラウンド (Reset) Rebuy --> Init : リバイ/アドオン完了 End --> [*] : ゲーム終了 ``` ### 3.4 Hearts フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Pass : Reset() Pass --> Play : カード交換完了
(または交換なしラウンド) Play --> TrickEnd : 4人全員カード出し完了 TrickEnd --> Play : 次トリック開始 TrickEnd --> RoundEnd : 13トリック完了 RoundEnd --> Pass : 次ラウンド開始 RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] ``` ### 3.5 Spades フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bid : Reset() Bid --> Play : 4人全員ビッド完了 Play --> TrickEnd : 4人全員カード出し完了 TrickEnd --> Play : 次トリック開始 TrickEnd --> RoundEnd : 13トリック完了 RoundEnd --> Bid : 次ラウンド開始 RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] ``` ### 3.6 Doubt フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Play : Reset() Play --> DoubtPhase : カードを出す DoubtPhase --> Play : ダウトスキップ → 次プレイヤー DoubtPhase --> Play : ダウト実行 → ペナルティ処理 → 次プレイヤー DoubtPhase --> End : ダウト成功 & 全カード消化 Play --> End : 手札0枚 (勝利) End --> [*] ``` ### 3.7 Memory フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Flip1 : Reset() Flip1 --> Flip2 : 1枚目めくり Flip2 --> Result : 2枚目めくり Result --> Flip1 : Next() → 次プレイヤーのターン Result --> GameEnd : 全ペアマッチ GameEnd --> [*] note right of Flip1 : MemoryPhaseFlip1 = 0 note right of Flip2 : MemoryPhaseFlip2 = 1 note right of Result : MemoryPhaseResult = 2 ``` ### 3.8 Klondike / FreeCell / Spider / Pyramid / TriPeaks / Golf / ClockSolitaire フェーズ遷移 7つのソリティア系ゲームは共通のフェーズ構造を持ちます。 ```mermaid stateDiagram-v2 [*] --> Playing : Reset() Playing --> Playing : Move / Draw / Deal / Remove / Step / Undo Playing --> GameClear : 全カードをFoundation/Pyramid/Tableau除去完了または全表向き Playing --> GameClear : Autocomplete成功 (Klondike/FreeCell/Spider のみ) Playing --> GameOver : GiveUp または4枚目のK表向き(ClockSolitaire) GameClear --> [*] GameOver --> [*] note right of Playing : Klondike/FreeCell/Spider/Pyramid/TriPeaks/Golf/ClockSolitaire 共通 Phase = 0 note right of GameClear : Phase = 1 note right of GameOver : Phase = 2 ``` Pyramid 固有のアクション: `Draw` / `RemovePair` / `RemoveKing` / `RemoveWithWaste` / `RemoveWasteKing` / `Undo`。クリア条件はピラミッドの28枚全除去。 TriPeaks 固有のアクション: `Draw` / `Remove` / `Undo`。除去条件はウェイストトップ±1ランク(K-Aラップ)。クリア条件はタブローの28枚全除去。 Golf 固有のアクション: `Draw` / `Remove` / `Undo`。除去条件はウェイストトップ±1ランク(K-Aラップ)。7列×5段の35枚全除去でクリア。 ClockSolitaire 固有のアクション: `Step` / `AutoPlay`。52枚を13山に4枚ずつ配り、ランクに対応する山へ移動させる完全自動ゲーム。4枚目のKが表向きになる前に全カードが表向きになるとクリア。 各ゲームのフェーズ定数名: `KlondikePhasePlaying` / `FreeCellPhasePlaying` / `SpiderPhasePlaying` / `PyramidPhasePlaying` / `TriPeaksPhasePlaying` / `GolfPhasePlaying` / `ClockSolitairePhasePlaying` = 0、`…GameClear` = 1、`…GameOver` = 2。 ### 3.9 CrazyEights フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Play : Reset() Play --> Play : カードを出す / ドローする Play --> ChooseSuit : 8を出す ChooseSuit --> Play : スート選択完了 Play --> RoundEnd : 手札0枚 (勝ち上がり) RoundEnd --> Play : NextRound() RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] ``` ### 3.10 GinRummy フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Draw : Reset() Draw --> Discard : カードを引く (山札 or 捨て札) Discard --> Draw : カードを捨てる → 次プレイヤー Discard --> Layoff : ノック宣言 Discard --> RoundEnd : ジン宣言 Layoff --> RoundEnd : レイオフ完了 RoundEnd --> Draw : NextRound() RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] ``` ### 3.11 Baccarat フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bet : Reset() Bet --> End : ベット → 自動カード配布 → 結果判定 End --> Bet : 次ラウンド (Reset) End --> [*] : チップ0 (ゲーム終了) ``` ### 3.12 Napoleon フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bid : Reset() Bid --> Trump : ナポレオン決定 Trump --> Exchange : 切り札宣言 + 副官指名 Exchange --> Play : キティ交換完了 Play --> TrickEnd : 4人全員カード出し完了 TrickEnd --> Play : 次トリック開始 TrickEnd --> RoundEnd : 13トリック完了 RoundEnd --> Bid : 次ラウンド開始 RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] ``` ### 3.13 IndianPoker フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Init : 初期状態 Init --> Ante : Reset() Ante --> Betting : アンティ投入・カード配布 Betting --> Showdown : 全員アクション完了 Betting --> Showdown : 1人以外全員フォールド Showdown --> Ante : 次ハンド開始 Showdown --> End : プレイヤーのチップが0 End --> [*] ``` ### 3.14 VideoPoker フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bet : Reset() Bet --> Draw : ベット(1-5コイン) Draw --> Result : ホールド選択 → カード交換 → 役判定 Result --> Bet : 次ラウンド (Reset) Result --> [*] : チップ0 (ゲーム終了) note right of Bet : VideoPokerPhaseBet = 1 note right of Draw : VideoPokerPhaseDraw = 2 note right of Result : VideoPokerPhaseResult = 3 ``` ### 3.15 Euchre フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> PickUp : Reset() PickUp --> CallTrump : 全員パス PickUp --> Discard : オーダーアップ(ディーラーが拾う) CallTrump --> Discard : トランプ宣言(ディーラーが拾う) CallTrump --> PickUp : 全員パス(ディーラー強制コール) Discard --> Play : ディーラーが1枚捨てる Play --> TrickEnd : 参加者全員カード出し完了 TrickEnd --> Play : 次トリック開始 TrickEnd --> RoundEnd : 5トリック完了 RoundEnd --> PickUp : 次ラウンド開始 RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] note right of PickUp : EuchrePhasePickUp = 0 note right of CallTrump : EuchrePhaseCallTrump = 1 note right of Discard : EuchrePhaseDiscard = 2 note right of Play : EuchrePhasePlay = 3 note right of TrickEnd : EuchrePhaseTrickEnd = 4 note right of RoundEnd : EuchrePhaseRoundEnd = 5 note right of GameEnd : EuchrePhaseGameEnd = 6 ``` ### 3.16 Cribbage フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Discard : Reset() Discard --> Cut : 両プレイヤーが2枚ずつクリブに捨てる Cut --> Pegging : スターターカード公開 Pegging --> Pegging : Peg / Go (ペギング継続) Pegging --> Show : 全カード使用済み Show --> Show : ShowNext (次のスコア表示) Show --> RoundEnd : 全スコア表示完了 RoundEnd --> Discard : NextRound (次のラウンド) RoundEnd --> GameEnd : 121点到達 Pegging --> GameEnd : ペギング中に121点到達 Show --> GameEnd : ショー中に121点到達 note right of Discard : CribbagePhaseDiscard = 0 note right of Cut : CribbagePhaseCut = 1 note right of Pegging : CribbagePhasePegging = 2 note right of Show : CribbagePhaseShow = 3 note right of RoundEnd : CribbagePhaseRoundEnd = 4 note right of GameEnd : CribbagePhaseGameEnd = 5 ``` ### 3.17 ShortDeck フェーズ遷移 Short Deck Hold'em は Texas Hold'em と同一のフェーズ遷移を使用します([3.3 Texas Hold'em フェーズ遷移](#33-texas-holdem-フェーズ遷移)を参照)。 主な違いはデッキ構成(36枚、2〜5除去)とハンドランキング(フラッシュ > フルハウス、最低ストレート = A-6-7-8-9)のみで、フェーズ遷移ロジックは���通です。 ### 3.18 ThreeCard フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bet : Reset() Bet --> Action : アンティベット → 3枚配布 Action --> End : プレイ → ディーラー公開 → 結果判定 Action --> End : フォールド → ベット没収 End --> Bet : 次ラウンド (Reset) End --> [*] : チップ0 (ゲーム終了) note right of Bet : ThreeCardPhaseBet = 1 note right of Action : ThreeCardPhaseAction = 2 note right of End : ThreeCardPhaseEnd = 3 ``` ### 3.19 OhHell フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bid : Reset() Bid --> Play : 4人全員ビッド完了 Play --> TrickEnd : 4人全員カード出し完了 TrickEnd --> Play : 次トリック開始 TrickEnd --> RoundEnd : 全トリック完了 RoundEnd --> Bid : 次ラウンド開始 RoundEnd --> GameEnd : 全ラウンド完了 GameEnd --> [*] note right of Bid : OhHellPhaseBid = 0 note right of Play : OhHellPhasePlay = 1 note right of TrickEnd : OhHellPhaseTrickEnd = 2 note right of RoundEnd : OhHellPhaseRoundEnd = 3 note right of GameEnd : OhHellPhaseGameEnd = 4 ``` ### 3.20 Bridge フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bid : Reset() Bid --> Play : コントラクト確定(3連続パス) → ダミー公開 Play --> TrickEnd : 4人全員カード出し完了 TrickEnd --> Play : 次トリック開始 TrickEnd --> RoundEnd : 13トリック完了 RoundEnd --> Bid : 次ラウンド開始 RoundEnd --> GameEnd : ラバー完了(2ゲーム先勝) Bid --> RoundEnd : 4人全員パス(パスアウト) GameEnd --> [*] note right of Bid : BridgePhaseBid = 0 note right of Play : BridgePhasePlay = 1 note right of TrickEnd : BridgePhaseTrickEnd = 2 note right of RoundEnd : BridgePhaseRoundEnd = 3 note right of GameEnd : BridgePhaseGameEnd = 4 ``` ### 3.21 Pineapple フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Init : Reset() Init --> PreFlop : ブラインド + ホールカード3枚配布 PreFlop --> Flop : ベッティング完了 PreFlop --> Showdown : 1人以外 Fold Flop --> Discard : ベッティング完了 Flop --> Showdown : 1人以外 Fold Discard --> Turn : 全員ディスカード完了 Turn --> River : ベッティング完了 Turn --> Showdown : 1人以外 Fold River --> Showdown : ベッティング完了 Showdown --> End : 勝者決定 End --> Rebuy : リバイ/アドオン有効 End --> Init : 次ラウンド (Reset) Rebuy --> Init : リバイ/アドオン完了 End --> [*] : ゲーム終了 note right of Init : PineapplePhaseInit = 0 note right of PreFlop : PineapplePhasePreFlop = 1 note right of Flop : PineapplePhaseFlop = 2 note right of Discard : PineapplePhaseDiscard = 8 note right of Turn : PineapplePhaseTurn = 3 note right of River : PineapplePhaseRiver = 4 note right of Showdown : PineapplePhaseShowdown = 5 note right of End : PineapplePhaseEnd = 6 note right of Rebuy : PineapplePhaseRebuy = 7 ``` ### 3.22 Speed フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Play : Reset() Play --> Play : カードを場札に出す + CPU自動プレイ Play --> Stuck : 両プレイヤーが出せるカードなし Stuck --> Play : Flip() → 新しい場札をめくる Stuck --> GameEnd : 山札なし(フリップ不可) Play --> GameEnd : 手札+山札が空(勝利) GameEnd --> [*] note right of Play : SpeedPhasePlay = 0 note right of Stuck : SpeedPhaseStuck = 1 note right of GameEnd : SpeedPhaseGameEnd = 2 ``` ### 3.23 GoFish フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Play : Reset() Play --> Play : Ask成功 → もう一度要求 + CPU自動プレイ Play --> Play : Go Fish → 山札から引く + CPU自動プレイ Play --> GameEnd : 全13ブック完成 or 山札枯渇 GameEnd --> [*] note right of Play : GoFishPhasePlay = 0 note right of GameEnd : GoFishPhaseGameEnd = 1 ``` ### 3.24 Canasta フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Draw : Reset() Draw --> Meld : 山札から引く or 捨て札の山を取る Meld --> Discard : メルド実行 or スキップ Meld --> RoundEnd : メルド後手札0枚 + カナスタあり → 上がり Discard --> Draw : カードを捨てる → 次プレイヤー Discard --> RoundEnd : 上がり (カナスタ必須) Draw --> RoundEnd : 山札枯渇 → 引き分け RoundEnd --> Draw : NextRound() RoundEnd --> GameEnd : 目標点到達 GameEnd --> [*] note right of Draw : CanastaPhaseDraw = 0 note right of Meld : CanastaPhaseMeld = 1 note right of Discard : CanastaPhaseDiscard = 2 note right of RoundEnd : CanastaPhaseRoundEnd = 3 note right of GameEnd : CanastaPhaseGameEnd = 4 ``` ### 3.25 Pinochle フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Bid : Reset() Bid --> Trump : ビッド勝者決定 Bid --> Trump : 全員パス → ディーラー強制ビッド Trump --> Meld : トランプスート宣言 Meld --> Play : メルド確認 (ConfirmMelds) Play --> TrickEnd : 4人プレイ完了 TrickEnd --> Play : NextTrick() → 次トリック TrickEnd --> RoundEnd : 12トリック完了 → 得点計算 RoundEnd --> Bid : NextRound() RoundEnd --> GameEnd : ポイント上限到達 GameEnd --> [*] note right of Bid : PinochlePhaseBid = 0 note right of Trump : PinochlePhaseTrump = 1 note right of Meld : PinochlePhaseMeld = 2 note right of Play : PinochlePhasePlay = 3 note right of TrickEnd : PinochlePhaseTrickEnd = 4 note right of RoundEnd : PinochlePhaseRoundEnd = 5 note right of GameEnd : PinochlePhaseGameEnd = 6 ``` ### 3.26 PigsTail フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Play : Reset() Play --> Play : Draw() → スート不一致 or 一致ペナルティ → CPU自動プレイ Play --> GameEnd : 山札が空 GameEnd --> [*] note right of Play : gameEndFlag = false note right of GameEnd : gameEndFlag = true ``` ### 3.27 SevenCardStud フェーズ遷移 ```mermaid stateDiagram-v2 [*] --> Init : Reset() Init --> ThirdSt : アンティ + ホールカード2枚 + ドアカード1枚配布 + ブリングイン ThirdSt --> FourthSt : ベッティング完了 ThirdSt --> Showdown : 1人以外 Fold FourthSt --> FifthSt : ベッティング完了 FourthSt --> Showdown : 1人以外 Fold FifthSt --> SixthSt : ベッティング完了 FifthSt --> Showdown : 1人以外 Fold SixthSt --> SeventhSt : ベッティング完了 SixthSt --> Showdown : 1人以外 Fold SeventhSt --> Showdown : ベッティング完了 Showdown --> End : 勝者決定 End --> Rebuy : リバイ/アドオン有効 End --> Init : 次ラウンド (Reset) Rebuy --> Init : リバイ/アドオン完了 End --> [*] : ゲーム終了 note right of Init : SevenCardStudPhaseInit = 0 note right of ThirdSt : SevenCardStudPhaseThirdSt = 1 note right of FourthSt : SevenCardStudPhaseFourthSt = 2 note right of FifthSt : SevenCardStudPhaseFifthSt = 3 note right of SixthSt : SevenCardStudPhaseSixthSt = 4 note right of SeventhSt : SevenCardStudPhaseSeventhSt = 5 note right of Showdown : SevenCardStudPhaseShowdown = 6 note right of End : SevenCardStudPhaseEnd = 7 note right of Rebuy : SevenCardStudPhaseRebuy = 8 ``` **注:** OldMaid・Daifugo・Sevens は明示的なフェーズ定数を持たず、ターン制で進行します (currentTurn が巡回し、全プレイヤーの手札が0枚またはランク確定で終了)。
# フロントエンド設計ドキュメント (UML) 本ドキュメントは go_trumpcards フロントエンド (React + TypeScript) の設計をMermaid記法で可視化したものです。 ## 目次 - [1. クラス図](#1-クラス図) - [1.1 型定義 (Card・Response・Phase)](#11-型定義-cardresponsephase) - [1.2 API クライアント層](#12-api-クライアント層) - [1.3 Hook 層 (共通Hook)](#13-hook-層-共通hook) - [1.4 Hook 層 (ゲーム固有Hook)](#14-hook-層-ゲーム固有hook) - [1.5 コンポーネント層](#15-コンポーネント層) - [1.6 ページコンポーネント層](#16-ページコンポーネント層) - [1.7 i18n・プロバイダー・ルーティング](#17-i18nプロバイダールーティング) - [2. シーケンス図](#2-シーケンス図) - [2.1 ゲームアクション実行フロー](#21-ゲームアクション実行フロー) - [2.2 CPUリプレイアニメーションフロー](#22-cpuリプレイアニメーションフロー) - [2.3 ゲーム初期化フロー](#23-ゲーム初期化フロー) - [2.4 VideoPokerPage フェーズ別レンダリングフロー](#24-videopokerpage-フェーズ別レンダリングフロー) - [2.5 ThreeCardPage フェーズ別レンダリングフロー](#25-threecardpage-フェーズ別レンダリングフロー) - [2.6 OhHellPage フェーズ別レンダリングフロー](#26-ohhellpage-フェーズ別レンダリングフロー) - [2.7 BridgePage フェーズ別レンダリングフロー](#27-bridgepage-フェーズ別レンダリングフロー) - [2.8 PineapplePage フェーズ別レンダリングフロー](#28-pineapplepage-フェーズ別レンダリングフロー) - [2.9 SpeedPage フェーズ別レンダリングフロー](#29-speedpage-フェーズ別レンダリングフロー) - [2.10 GoFishPage フェーズ別レンダリングフロー](#210-gofishpage-フェーズ別レンダリングフロー) - [2.11 CanastaPage フェーズ別レンダリングフロー](#211-canastapage-フェーズ別レンダリングフロー) - [2.12 PinochlePage フェーズ別レンダリングフロー](#212-pinochlepage-フェーズ別レンダリングフロー) - [2.13 PigsTailPage フェーズ別レンダリングフロー](#213-pigstailpage-フェーズ別レンダリングフロー) - [2.14 SevenCardStudPage フェーズ別レンダリングフロー](#214-sevencardstudpage-フェーズ別レンダリングフロー) - [2.15 CLIモード コマンド実行フロー](#215-cliモード-コマンド実行フロー) - [3. ステートマシン図](#3-ステートマシン図) - [3.1 ゲームページ表示状態](#31-ゲームページ表示状態) - [3.2 カード選択状態 (useCardSelection)](#32-カード選択状態-usecardselection) - [3.3 確認ダイアログ状態 (useConfirmDialog)](#33-確認ダイアログ状態-useconfirmdialog) - [3.4 アクションログ状態 (useActionLog)](#34-アクションログ状態-useactionlog) - [3.5 ゲーム設定パネル状態](#35-ゲーム設定パネル状態) - [3.6 チュートリアル状態 (useTutorial)](#36-チュートリアル状態-usetutorial) - [3.7 CLIモード状態 (useCliMode + useCliGame)](#37-cliモード状態-useclimode--usecligame) --- ## 1. クラス図 ### 1.1 型定義 (Card・Response・Phase) ```mermaid classDiagram class Card { +string design +number value } class ActionLogEntry { +number turnNumber +number playerIdx +string actionType +string detail +Card[] cards } class BlackJackResponse { +object dealer +object[] players +number phase +string message +string messageCode +object messageParams } class HeartsResponse { +object[] players +object[] trickCards +number[][] scores +number phase +string message +string messageCode +object messageParams } class KlondikeResponse { +object[][] tableau +Card[][] foundation +Card[] stock +Card[] waste +number phase +number moves +number score +string message +string messageCode +object messageParams } class MemoryResponse { +object[] board +object[] players +number phase +number currentPlayer +string message +string messageCode +object messageParams } class TutorialStep { +string target +string messageKey +TutorialPlacement placement +TutorialAdvanceOn advanceOn +Function onEnter } class TutorialConfig { +string gameName +TutorialStep[] steps } TutorialConfig --> TutorialStep : contains class PyramidResponse { +object[][] pyramid +Card[] waste +number stockCount +number phase +number moveCount +boolean canUndo +boolean isStalemate +string message +string messageCode +object messageParams } class TriPeaksResponse { +object[][] tableau +Card[] waste +number stockCount +number phase +number moveCount +boolean canUndo +boolean isStalemate +string message +string messageCode +object messageParams } class GolfResponse { +object[][] layout +Card[] waste +number stockCount +number phase +number moveCount +boolean canUndo +boolean isStalemate +string message +string messageCode +object messageParams } class ClockSolitaireResponse { +object[][] piles +number faceUpCount +number phase +number stepCount +Card currentCard +string message +string messageCode +object messageParams } class CribbageResponse { +object[] players +number phase +number roundNumber +number currentPlayerIdx +number dealerIdx +Card[] crib +Card starter +number pegCount +Card[] pegPlayedCards +number showPhaseStep +object[] handScoreDetails +boolean gameEndFlag +number winnerIdx +string message +string messageCode +object messageParams +object config } class SpeedResponse { +object[] players +Card[] centerPiles +number phase +number currentTurn +number winnerIdx +object[] cpuActions +string message +string messageCode +object messageParams } class GoFishResponse { +object[] players +number phase +number currentTurn +number winnerIdx +number stockCount +object humanAction +object[] cpuActions +string message +string messageCode +object messageParams } class CanastaResponse { +CanastaPlayerData[] players +number phase +number roundNumber +number currentPlayerIdx +Card discardTop +number drawPileCount +number discardPileCount +boolean isFrozen +boolean gameEndFlag +number winnerIdx +CanastaConfig config +string message +string messageCode +object messageParams } class PinochleResponse { +PinochlePlayerData[] players +number phase +number roundNumber +number trickNumber +number currentPlayerIdx +number bidPlayerIdx +number trumpSuit +number highestBid +number highestBidder +PinochleTrickCard[] currentTrick +number[] teamScores +boolean gameEndFlag +number winnerTeam +PinochleMeldData[][] playerMelds +number[] validPlayIndices +PinochleConfig config +string message } class PigsTailResponse { +object[] players +Card[] centerPile +number circlePileCount +number phase +number currentTurn +number winnerIdx +object[] cpuActions +string message +string messageCode +object messageParams } class SevenCardStudResponse { +object[] players (holeCards, doorCards) +number pot +object[] sidePots +number dealerIdx +number currentTurn +number phase +boolean gameEndFlag +number lastBet +number minRaise +number bettingLimit +number raiseCount +number maxBetAmount +object[] roundResults +object[] cpuActions +number handCount +number ante +number bringIn +number smallBet +number bigBet +boolean tournamentMode +number anteLevelHands +number anteMultiplier +number tableSize +number bringInPlayerIdx +boolean rebuyAvailable +boolean addonAvailable +string message +string messageCode +object messageParams } note for BlackJackResponse "各ゲームが固有のResponse型を持つ\n(全39ゲーム分存在)\n共通フィールド: message, messageCode, messageParams" ``` **フェーズ定数 (全ゲーム)** ```mermaid classDiagram class BjPhase { <> BET = 1 DEAL = 2 INSURANCE = 3 ACTION = 4 END = 5 EARLY_SURRENDER = 6 } class PokerPhase { <> INIT = 0 DEAL = 1 EXCHANGE = 2 SECOND_BET = 3 END = 4 } class HoldemPhase { <> INIT = 0 PRE_FLOP = 1 FLOP = 2 TURN = 3 RIVER = 4 SHOWDOWN = 5 END = 6 REBUY = 7 } class HeartsPhase { <> PASS = 0 PLAY = 1 TRICK_END = 2 ROUND_END = 3 GAME_END = 4 } class SpadesPhase { <> BID = 0 PLAY = 1 TRICK_END = 2 ROUND_END = 3 GAME_END = 4 } class MemoryPhase { <> FLIP1 = 0 FLIP2 = 1 RESULT = 2 GAME_END = 3 } class KlondikePhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class FreeCellPhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class SpiderPhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class CrazyEightsPhase { <> PLAY = 0 CHOOSE_SUIT = 1 ROUND_END = 2 GAME_END = 3 } class GinRummyPhase { <> DRAW = 0 DISCARD = 1 LAYOFF = 2 ROUND_END = 3 GAME_END = 4 } class BaccaratPhase { <> BET = 1 END = 2 } class NapoleonPhase { <> BID = 0 TRUMP_DECLARATION = 1 KITTY_EXCHANGE = 2 PLAY = 3 TRICK_END = 4 ROUND_END = 5 GAME_END = 6 } class IndianPokerPhase { <> INIT = 0 ANTE = 1 BETTING = 2 SHOWDOWN = 3 END = 4 } class VideoPokerPhase { <> BET = 1 DRAW = 2 RESULT = 3 } class EuchrePhase { <> PICK_UP = 0 CALL_TRUMP = 1 DISCARD = 2 PLAY = 3 TRICK_END = 4 ROUND_END = 5 GAME_END = 6 } class PyramidPhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class TriPeaksPhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class GolfPhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class ClockSolitairePhase { <> PLAYING = 0 GAME_CLEAR = 1 GAME_OVER = 2 } class PigsTailPhase { <> PIGTAIL_PHASE_PLAY = 0 PIGTAIL_PHASE_END = 1 } class CribbagePhase { <> DISCARD = 0 CUT = 1 PEGGING = 2 SHOW = 3 ROUND_END = 4 GAME_END = 5 } class OhHellPhase { <> BID = 0 PLAY = 1 TRICK_END = 2 ROUND_END = 3 GAME_END = 4 } class BridgePhase { <> BID = 0 PLAY = 1 TRICK_END = 2 ROUND_END = 3 GAME_END = 4 } class ThreeCardPhase { <> BET = 1 ACTION = 2 END = 3 } class SpeedPhase { <> PLAY = 0 STUCK = 1 GAME_END = 2 } class GoFishPhase { <> PLAY = 0 GAME_END = 1 } class CanastaPhase { <> DRAW = 0 MELD = 1 DISCARD = 2 ROUND_END = 3 GAME_END = 4 } class PinochlePhase { <> BID = 0 TRUMP = 1 MELD = 2 PLAY = 3 TRICK_END = 4 ROUND_END = 5 GAME_END = 6 } class SevenCardStudPhase { <> INIT = 0 THIRD_STREET = 1 FOURTH_STREET = 2 FIFTH_STREET = 3 SIXTH_STREET = 4 SEVENTH_STREET = 5 SHOWDOWN = 6 END = 7 REBUY = 8 } note for KlondikePhase "KlondikePhase, FreeCellPhase, SpiderPhase, PyramidPhase, TriPeaksPhase, GolfPhase, ClockSolitairePhase は、\nそれぞれ同一の値を持つ別定数です" ``` ### 1.2 API クライアント層 ```mermaid classDiagram class gameApi { -string sessionId +postJson~T~(url, body) Promise~T~ +gameExec~T~(game, body) Promise~T~ } class BlackJackApi { +exec(cmd, args?, config?) Promise~BlackJackResponse~ } class PokerApi { +exec(cmd, args?, config?) Promise~PokerResponse~ } class HeartsApi { +exec(cmd, args?, config?) Promise~HeartsResponse~ } class KlondikeApi { +exec(cmd, args?, config?) Promise~KlondikeResponse~ } class PyramidApi { +run(cmd, card1?, card2?) Promise~PyramidResponse~ } class TriPeaksApi { +run(cmd, row?, col?) Promise~TriPeaksResponse~ } class GolfApi { +run(cmd, col?) Promise~GolfResponse~ } class ClockSolitaireApi { +run(cmd) Promise~ClockSolitaireResponse~ } class CribbageApi { +run(cmd, args?, config?) Promise~CribbageResponse~ } class actionLogApi { +blackjack() Promise~ActionLogResponse~ +poker() Promise~ActionLogResponse~ ...全39ゲーム() } BlackJackApi --> gameApi : uses postJson/gameExec PokerApi --> gameApi : uses postJson/gameExec HeartsApi --> gameApi : uses postJson/gameExec KlondikeApi --> gameApi : uses postJson/gameExec PyramidApi --> gameApi : uses postJson/gameExec TriPeaksApi --> gameApi : uses postJson/gameExec ClockSolitaireApi --> gameApi : uses postJson/gameExec CribbageApi --> gameApi : uses postJson/gameExec actionLogApi --> gameApi : uses gameExec class SpeedApi { +run(cmd, args?) Promise~SpeedResponse~ } SpeedApi --> gameApi : uses postJson/gameExec class GoFishApi { +run(cmd, args?) Promise~GoFishResponse~ } GoFishApi --> gameApi : uses postJson/gameExec class CanastaApi { +run(cmd, args?) Promise~CanastaResponse~ } CanastaApi --> gameApi : uses postJson/gameExec class PigsTailApi { +run(cmd) Promise~PigsTailResponse~ } PigsTailApi --> gameApi : uses postJson/gameExec note for gameApi "全APIリクエストにsessionIdを自動付与\n各ゲームAPIは cmd ベースの統一形式" note for BlackJackApi "全39ゲーム分のAPI Objectが存在\n(blackjack, poker, oldmaid, daifugo,\nsevens, doubt, holdem, omaha, shortdeck,\npineapple, hearts, memory, klondike, freecell,\nbaccarat, spades, crazyeights, ginrummy, canasta,\nspider, napoleon, indianpoker, videopoker,\ndeuceswild, jokerpoker, euchre, pyramid, tripeaks,\ncribbage, threecard, ohhell, bridge, speed,\ngofish, pinochle, golf, pigtail,\nsevencardstud, clocksolitaire)" ``` ### 1.3 Hook 層 (共通Hook) ```mermaid classDiagram class useGamePageSetup { +TFunction t +TFunction tc +ActionLogEntry[] actionLog +Function showActionLog +Function hideActionLog +boolean confirmOpen +Function requestConfirm +Function confirmReset +Function cancelReset } class useGameApi~TState_TArgs~ { +TState data +boolean isLoading +Error error +Function exec } class useGameConfig~T~ { +T config +Function handleConfigChange +Function handleToggle } class useCardSelection { +number[] selected +Function toggle +Function clear } class usePhaseNames { +Record~number_string~ phaseNames } class useActionLog { +ActionLogEntry[] entries +boolean isOpen +Function show +Function hide } class useConfirmDialog { +boolean isOpen +Function requestConfirm +Function confirm +Function cancel } class useCardDimensions { +number cardWidth +number cardHeight +number cardOverlap +number cpuCardWidth +number footerCardWidth +number solitaireMinColWidth } note for useCardDimensions "3-tier responsive = mobile/desktop/largeDesktop (640px/1024px)" class useIsLargeDesktop { +boolean isLargeDesktop } note for useIsLargeDesktop "Returns true when viewport >= 1024px" class useActionKeyboardNav { +(bindings: KeyBinding[]) void } class useCardKeyboardNav { +(cardCount, onToggle, onConfirm, ...) void } class useGameSound { +Function playSound +boolean enabled +Function toggle } class useReducedMotion { +boolean prefersReducedMotion } class useTutorial { +boolean isActive +number currentStepIndex +TutorialStep currentStep +number totalSteps +boolean isCompleted +boolean canResume +Function start +Function restart +Function next +Function skip } class useFirstVisit { +boolean shouldShowDialog +Function dismiss +Function dismissPermanently } class useGameHint { +boolean hintEnabled +Function setHintEnabled +HintResult hint } class useTutorialProgress { +GameProgress[] games +number completedCount +number totalCount } class useLocalStorageToggle { +boolean value +Function setValue } class useCardGesture { +Function onSwipe +Function onTap } class useHaptics { +Function vibrate } class useKlondikeTimer { +number elapsed +Function start +Function stop +Function reset } class useProfilePersistence { +Function loadProfile +Function saveProfile } class useFavoriteGames { +string[] favorites +Function toggle +Function isFavorite } class useRecentGames { +string[] recentGames +Function addRecent } class useCliMode { +boolean cliEnabled +Function toggleCli +CliLogEntry[] logEntries +Function addInput +Function addOutput +Function addError +Function clearLog } note for useCliMode "localStorage persistence per game" class useCliGame~TState_TArgs~ { +Function handleCommand } note for useCliGame "Orchestrates command parse -> exec -> format -> log" useGamePageSetup --> useActionLog : composes useGamePageSetup --> useConfirmDialog : composes useTutorial ..> useReducedMotion : optional useGameHint --> useLocalStorageToggle : uses useCliMode --> useLocalStorageToggle : uses useCliGame --> useCliMode : reads logEntries ``` ### 1.4 Hook 層 (ゲーム固有Hook) ```mermaid classDiagram class useBlackJackGame { +BlackJackResponse state +Function handleBet +Function handleHit +Function handleStand +Function handleDoubleDown +Function handleSplit +Function handleInsurance +Function handleSurrender +Function handleReset } class useHeartsGame { +HeartsResponse state +number[] selectedCards +Function handlePass +Function handlePlay +Function handleNextTrick +Function handleNextRound +Function handleHint +Function handleReset } class useKlondikeGame { +KlondikeResponse state +MoveZone source +MoveZone target +Function handleDraw +Function handleMove +Function handleHint +Function handleUndo +Function handleAutocomplete +Function handleReset } class useMemoryGame { +MemoryResponse state +Function handleFlip +Function handleNext +Function handleReset } class useDoubtGame { +DoubtResponse state +number countdown +number[] selectedCards +Function handlePlay +Function handleDoubt +Function handleSkip +Function handleReset } useBlackJackGame --> useGameApi : uses useHeartsGame --> useGameApi : uses useHeartsGame --> useCardSelection : uses class usePyramidGame { +PyramidResponse state +Function handleDraw +Function handleRemove +Function handleHint +Function handleUndo +Function handleReset } class useTriPeaksGame { +TriPeaksResponse state +Function handleDraw +Function handleRemove +Function handleHint +Function handleUndo +Function handleReset } class useGolfGame { +GolfResponse state +Function handleDraw +Function handleSelectCard +Function handleHint +Function handleUndo +Function handleReset } class useClockSolitaireGame { +ClockSolitaireResponse state +Function handleStep +Function handleAutoPlay +Function handleReset } class usePigsTailGame { +PigsTailResponse state +Function handleDraw +Function handleReset } class useCribbageGame { +CribbageResponse state +Function handleDiscard +Function handlePeg +Function handleGo +Function handleShowNext +Function handleNextRound +Function handleReset } useKlondikeGame --> useGameApi : uses usePyramidGame --> useGameApi : uses useTriPeaksGame --> useGameApi : uses useGolfGame --> useGameApi : uses useClockSolitaireGame --> useGameApi : uses usePigsTailGame --> useGameApi : uses useCribbageGame --> useGameApi : uses useMemoryGame --> useGameApi : uses useDoubtGame --> useGameApi : uses useDoubtGame --> useCardSelection : uses class useOhHellGame { +OhHellResponse state +number[] selectedCards +Function handleBid +Function handlePlay +Function handleNextTrick +Function handleNextRound +Function handleHint +Function handleReset } useOhHellGame --> useGameApi : uses useOhHellGame --> useCardSelection : uses class useSpeedGame { +SpeedResponse state +Function handlePlay +Function handleFlip +Function handleHint +Function handleReset } useSpeedGame --> useGameApi : uses class useGoFishGame { +GoFishResponse state +Function handleAsk +Function handleReset } useGoFishGame --> useGameApi : uses class useCanastaGame { +CanastaResponse state +Function handleDrawStock +Function handleDrawDiscard +Function handleMeldSelected +Function handleSkipMeld +Function handleDiscard +Function handleGoOut +Function handleNextRound +Function handleReset } useCanastaGame --> useGameApi : uses note for useBlackJackGame "全39ゲーム分の固有Hookが存在\n各HookはuseGameApiで統一的にAPI呼出し\n必要に応じてuseCardSelectionを合成" ``` ### 1.5 コンポーネント層 ```mermaid classDiagram class NavBar { +gameCategories: GameCategory[] +言語切替 (JA/EN) +レスポンシブ ハンバーガーメニュー +カテゴリ折りたたみ (モバイル) +絵文字アイコン表示 } class GameRoute { +string path +string labelKey +string icon } class GameCategory { +string labelKey +string icon +GameRoute[] routes } class PhaseIndicator { +string phaseName +boolean isYourTurn +aria-live polite } class SettingsPanel { +ReactNode children +boolean open +ツールチップ付き設定項目 } class GameFooter { +ReactNode children +glassmorphismスタイル +safe-area対応 } class GameMessageBox { +string messageCode +object messageParams +i18n翻訳表示 } class ActionLogSection { +Function onShow +ActionLogEntry[] entries +モーダルパネル表示 } class ConfirmDialog { +string message +Function onConfirm +Function onCancel +フォーカストラップ +Escape キー対応 } class AnimatedCard { +Card card +number x +number y +number rotation +Framer Motion アニメーション } class CardImage { +Card card +number width +SVG カード画像 } class CpuPlayerCard { +string name +number chips +string status } class PokerTableLayout { +ReactNode communityCards +ReactNode cpuPlayers +string cpuAreaTutorial +string communityCardsTutorial +デスクトップ=3列グリッド / モバイル=縦並び } class ErrorBoundary { +ReactNode children +エラー時リトライボタン } class WinCelebration { +紙吹雪アニメーション } class ErrorAlert { +string message +Function onDismiss } class SkipNavLink { +アクセシビリティ スキップリンク } class GamePageHeading { +string title +visually-hidden h1 (WCAG 2.4.6) } class TutorialOverlay { +TutorialStep step +number stepIndex +number totalSteps +Function onNext +Function onSkip +SVG mask スポットライト +ResizeObserver 追従 +フォーカストラップ } class TutorialTooltip { +string message +TutorialPlacement placement +number stepIndex +number totalSteps +Function onNext +Function onSkip +glass-panel ツールチップ } class CliTerminal { +CliLogEntry[] logEntries +Function onCommand +boolean disabled +コマンド履歴 (up/down) +自動スクロール +黒背景 + 等幅フォント } class CliToggle { +boolean cliEnabled +Function onToggle +GUI/CLI アイコン切替 } TutorialOverlay --> TutorialTooltip : renders TutorialOverlay --> ConfirmDialog : reuses getFocusableElements ``` **マニュアル表示コンポーネント** ```mermaid classDiagram class ManualButton { +boolean open 状態管理 +ManualModal 表示制御 } class ManualModal { +boolean open +Function onClose +string gamePath +react-markdown レンダリング +remark-gfm テーブル対応 +bg-gray-900 不透明背景 +フォーカストラップ +Escape キー対応 } class MermaidBlock { +string code +dynamic import('mermaid') +SVG ダイアグラムレンダリング +エラー時フォールバック表示 } ManualButton --> ManualModal : renders ManualModal --> MermaidBlock : renders mermaid code blocks ``` ### 1.6 ページコンポーネント層 ```mermaid classDiagram class GamePage { <> useGamePageSetup() セットアップ usePhaseNames() フェーズ名 use[Game]Game() ゲームロジック render() ページ描画 } class BlackJackPage { +ベッティングUI +マルチハンド表示 +CPU プレイヤー表示 +サイドベット UI } class HeartsPage { +カード交換UI +トリック表示エリア +スコアテーブル +ヒントシステム } class KlondikePage { +タブロー列 (7列) +ファウンデーション (4山) +ストック/ウェイスト +Undo/ヒント/自動完成 } class MemoryPage { +52枚カードグリッド +プレイヤースコア表示 +カードフリップアニメーション } class DoubtPage { +カード選択UI +カウントダウンタイマー +ダウト判定表示 +フロントエンドヒント (useGameHint) } class NapoleonPage { +ビッドUI +切り札宣言UI +キティ交換UI +トリック表示エリア +スコアテーブル +ヒントシステム +フロントエンドヒント (useGameHint) } class IndianPokerPage { +他プレイヤーカード表示 +自分カード非表示(???) +ベッティングアクションボタン +ポット・サイドポット表示 +ショーダウン結果表示 } class VideoPokerPage { +配当表表示 +コインセレクター (1-5) +5枚カード表示 +カードクリックでホールド選択 +役名・配当表示 } class DeucesWildPage { +Deuces Wild配当表表示 +VideoPokerGameContent再利用 +ワイルドカード(2)ハイライト } class JokerPokerPage { +Joker Poker配当表表示 +VideoPokerGameContent再利用 +ジョーカーワイルドカード表示 } class EuchrePage { +チーム別スコア表示 +ビッドUI (オーダーアップ/パス/コール) +トリック表示エリア +切り札・ターンアップカード表示 +ゴーイングアローン選択 +ヒントシステム +フロントエンドヒント (useGameHint) } class PyramidPage { +ピラミッド表示 (7段) +ストック/ウェイスト +カード選択・ペア除去 +Undo/ヒント } class TriPeaksPage { +3ピークスタブロー表示 +ストック/ウェイスト +カード除去 (±1ランク) +Undo/ヒント } class CribbagePage { +プレイヤー情報・スコア表示 +スターターカード +ペギングエリア +ディスカード/ペグ操作 +ショーフェーズスコア詳細 } class BaccaratPage { +ベッティングUI (プレイヤー/バンカー/タイ) +サイドベット (ペア) +カード表示 +罫線 (Big Road) +ヒントシステム } class ThreeCardPage { +チップ表示 +アンティ/ペアプラスベット入力 +プレイヤー3枚カード表示 +ディーラー3枚カード表示 +プレイ/フォールドボタン +配当詳細表示(アンティ/プレイ/ボーナス/ペアプラス) +ヒントシステム } BlackJackPage --|> GamePage : follows pattern HeartsPage --|> GamePage : follows pattern KlondikePage --|> GamePage : follows pattern MemoryPage --|> GamePage : follows pattern DoubtPage --|> GamePage : follows pattern NapoleonPage --|> GamePage : follows pattern IndianPokerPage --|> GamePage : follows pattern VideoPokerPage --|> GamePage : follows pattern DeucesWildPage --|> GamePage : follows pattern JokerPokerPage --|> GamePage : follows pattern DeucesWildPage --> VideoPokerPage : reuses VideoPokerGameContent JokerPokerPage --> VideoPokerPage : reuses VideoPokerGameContent EuchrePage --|> GamePage : follows pattern PyramidPage --|> GamePage : follows pattern TriPeaksPage --|> GamePage : follows pattern class ShortDeckPage { +ホールデム系共通UI +36枚デッキ表示 +フラッシュ>フルハウス役順 +コミュニティカード表示 +ベッティングアクションボタン } ShortDeckPage --|> GamePage : follows pattern CribbagePage --|> GamePage : follows pattern BaccaratPage --|> GamePage : follows pattern ThreeCardPage --|> GamePage : follows pattern class OhHellPage { +プレイヤー情報・スコア表示 +切り札カード・スート表示 +ラウンド/トリック情報 +ビッド入力(フック制限表示) +トリックカード表示 +ヒントボタン +フロントエンドヒント (useGameHint) } OhHellPage --|> GamePage : follows pattern class BridgePage { +チーム別スコア表示(ラバー/ゲーム) +オークションUI (ビッドレベル/スート/パス/ダブル/リダブル) +ダミーハンド公開表示 +トリック表示エリア +切り札・コントラクト表示 +バルネラビリティ表示 +ヒントシステム +チュートリアル (TutorialProvider) } BridgePage --|> GamePage : follows pattern class PineapplePage { +ホールデム系共通UI +ホールカード3枚表示 +ディスカードフェーズUI (カード選択・捨てるボタン) +コミュニティカード表示 +ベッティングアクションボタン } PineapplePage --|> GamePage : follows pattern class SpeedPage { +中央場札2枚表示 +手札カード選択 +CPUカード裏向き表示 +フリップボタン (スタック時) +ヒントボタン +フロントエンドヒント (useGameHint) } SpeedPage --|> GamePage : follows pattern class GoFishPage { +CPUプレイヤーエリア (要求先選択) +ランク選択ボタン +ブック表示 +山札残り枚数 +CPU行動アニメーション } GoFishPage --|> GamePage : follows pattern class CanastaPage { +スコアテーブル表示 +メルド表示 (ナチュラル/ミックス/カナスタ) +赤3表示 +フリーズ状態表示 +捨て札の山ピックアップ (ペア選択) +メルドグループ選択 +チュートリアル (TutorialProvider) } CanastaPage --|> GamePage : follows pattern class PigsTailPage { +CPUプレイヤーエリア (手札枚数表示) +山札残り枚数 +場札トップカード表示 +引くボタン +CPU行動アニメーション } PigsTailPage --|> GamePage : follows pattern class SevenCardStudPage { +CPUプレイヤーエリア (ドアカード表示) +ホールカード・ドアカード表示 +ベッティングアクションボタン +ポット・サイドポット表示 +HUD統計表示 +マック/ショー選択 +リバイ/アドオン画面 +CPU行動アニメーション } SevenCardStudPage --|> GamePage : follows pattern class ClockSolitairePage { +13山を時計配置で表示 +表向き/伏せカード表示 +ステップ/自動再生ボタン +進捗インジケーター(表向き枚数) } ClockSolitairePage --|> GamePage : follows pattern GamePage --> PhaseIndicator : renders GamePage --> SettingsPanel : renders GamePage --> GameFooter : renders GamePage --> ActionLogSection : renders GamePage --> GameMessageBox : renders GamePage --> ConfirmDialog : renders GamePage --> ErrorAlert : renders GamePage --> GamePageHeading : renders GamePage --> ManualButton : renders GamePage --> PokerTableLayout : renders (Hold'em/Omaha/ShortDeck/Pineapple/SevenCardStud) PokerTableLayout --> CpuPlayerCard : wraps note for GamePage "全39ゲームページが同一パターンで構成\nuseGamePageSetup → ゲーム固有Hook → 描画" ``` ### 1.7 i18n・プロバイダー・ルーティング ```mermaid classDiagram class i18n { +string[] supportedLngs: ["ja", "en"] +string fallbackLng: "ja" +string[] namespaces +changeLanguage(lang) } class QueryProvider { +QueryClient client +retry: false } class App { +HashRouter +ErrorBoundary +NavBar +Routes (39ゲーム) } class gameCategories { +table: [BlackJack, Baccarat, ThreeCard] +poker: [Poker, Holdem, Omaha, ShortDeck, Pineapple, SevenCardStud, IndianPoker, VideoPoker, DeucesWild, JokerPoker] +trickTaking: [Hearts, Spades, OhHell, Euchre, Bridge, Napoleon] +matching: [OldMaid, Doubt, Daifugo, Sevens, CrazyEights, Speed, GoFish, Pinochle, PigsTail] +solitaire: [Klondike, FreeCell, Spider, Pyramid, TriPeaks, Golf, Memory, ClockSolitaire] +rummy: [GinRummy, Canasta, Cribbage] } class TutorialProvider { +TutorialConfig config +Function translateMessage +useTutorial 状態管理 +TutorialOverlay 自動レンダリング } App --> QueryProvider : wrapped by App --> i18n : initializes App --> gameCategories : routes from App --> NavBar : renders App --> GamePage : routes to 39 pages GamePage --> TutorialProvider : wraps (per-game) TutorialProvider --> TutorialOverlay : renders when active note for i18n "41名前空間: common + 39ゲーム固有 + tutorial\n翻訳ファイル: locales/{ja,en}/game.json" ``` --- ## 2. シーケンス図 ### 2.1 ゲームアクション実行フロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as GamePage participant Hook as useGameHook participant API as gameApi participant Server as Go REST API User->>Page: ボタンクリック (例: "Hit") Page->>Hook: handleHit() Hook->>API: api.blackjack.exec("hit", args, config) API->>API: sessionId 付与 API->>Server: POST /blackjack/exec
{"cmd":"hit","sessionId":"..."} Server-->>API: JSON レスポンス (BlackJackResponse) API-->>Hook: BlackJackResponse Hook->>Hook: setState(response) Hook-->>Page: 再レンダリング Page->>Page: フェーズに応じたUI更新 Page-->>User: 更新された画面表示 ``` ### 2.2 CPUリプレイアニメーションフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as GamePage participant Hook as useGameHook participant Replay as gameReplay participant API as gameApi User->>Page: アクション実行 Page->>Hook: handleAction() Hook->>API: exec(cmd) API-->>Hook: レスポンス (cpuActions含む) Hook->>Hook: setState(response) alt CPU アクションあり Hook->>Replay: runReplay(cpuActions) loop 各CPUアクション Replay->>Replay: delay(REPLAY_DELAY_MS) Replay->>Hook: onStep(action) Hook->>Hook: displayState更新 Hook-->>Page: 再レンダリング (アニメーション) end Replay-->>Hook: リプレイ完了 end Page-->>User: 最終状態表示 ``` ### 2.3 ゲーム初期化フロー ```mermaid sequenceDiagram participant Browser as ブラウザ participant App as App.tsx participant i18n as i18n participant QP as QueryProvider participant Page as GamePage participant Setup as useGamePageSetup participant Hook as useGameHook participant API as gameApi Browser->>App: ページアクセス (/#/blackjack) App->>i18n: 初期化 (言語検出) App->>QP: QueryClient 生成 App->>Page: ルーティング → BlackJackPage Page->>Setup: useGamePageSetup("blackjack") Setup->>Setup: useTranslation() 取得 Setup->>Setup: useActionLog() 初期化 Setup->>Setup: useConfirmDialog() 初期化 Page->>Hook: useBlackJackGame() Hook->>Hook: useGameApi() 初期化 Hook->>Hook: useGameConfig() 初期化 Note over Hook: useEffect → 初回 Reset Hook->>API: exec("reset", null, config) API-->>Hook: BlackJackResponse (初期状態) Hook->>Hook: setState(response) Hook-->>Page: 再レンダリング Page-->>Browser: ゲーム画面表示 ``` ### 2.4 VideoPokerPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as VideoPokerPage participant Hook as useVideoPokerGame participant API as gameApi Note over User,API: ベットフェーズ (phase=0) User->>Page: コインセレクターで数量選択 User->>Page: ベットボタンクリック Page->>Hook: handleBet(amount) Hook->>API: gameExec("bet", {amount}) API-->>Hook: VideoPokerResponse (phase=1, hand=5枚) Hook-->>Page: 再レンダリング → ドローフェーズUI Note over User,API: ドローフェーズ (phase=1) User->>Page: カードをクリックしてホールド選択 Page->>Page: ホールド状態をトグル表示 User->>Page: ドローボタンクリック Page->>Hook: handleHold(indices) Hook->>API: gameExec("hold", {indices}) API-->>Hook: VideoPokerResponse (phase=2, handRank, payout) Hook-->>Page: 再レンダリング → 結果フェーズUI Note over User,API: 結果フェーズ (phase=2) Page-->>User: 役名・配当表示 User->>Page: リセットボタンクリック Page->>Hook: handleReset() Hook->>API: gameExec("reset") API-->>Hook: VideoPokerResponse (phase=0) Hook-->>Page: 再レンダリング → ベットフェーズUI ``` ### 2.5 ThreeCardPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as ThreeCardPage participant Hook as useThreeCardGame participant API as gameApi Note over User,API: ベットフェーズ (phase=1) User->>Page: アンティ額・ペアプラス額を入力 User->>Page: ベットボタンクリック Page->>Hook: handleBet(amount, pairPlusBet) Hook->>API: gameExec("bet", {amount, pairPlusBet}) API-->>Hook: ThreeCardResponse (phase=2, playerHand=3枚) Hook-->>Page: 再レンダリング → アクションフェーズUI Note over User,API: アクションフェーズ (phase=2) User->>Page: プレイまたはフォールドボタンクリック Page->>Hook: handlePlay() / handleFold() Hook->>API: gameExec("play") / gameExec("fold") API-->>Hook: ThreeCardResponse (phase=3, result, payouts) Hook-->>Page: 再レンダリング → 結果フェーズUI Note over User,API: 結果フェーズ (phase=3) Page-->>User: 両手札・結果・配当詳細表示 User->>Page: リセットボタンクリック Page->>Hook: handleReset() Hook->>API: gameExec("reset") API-->>Hook: ThreeCardResponse (phase=1) Hook-->>Page: 再レンダリング → ベットフェーズUI ``` ### 2.6 OhHellPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as OhHellPage participant Hook as useOhHellGame participant API as gameApi Note over User,API: ビッドフェーズ (phase=0) User->>Page: ビッド数を入力 User->>Page: ビッドボタンクリック Page->>Hook: handleBid(bidValue) Hook->>API: gameExec("bid", {bid}) API-->>Hook: OhHellResponse (phase=1, 全員ビッド完了) Hook-->>Page: 再レンダリング → プレイフェーズUI Note over User,API: プレイフェーズ (phase=1) User->>Page: 手札カードをクリック User->>Page: 出すボタンクリック Page->>Hook: handlePlay(cardIndex) Hook->>API: gameExec("play", {cardIndex}) API-->>Hook: OhHellResponse (phase=2, トリック完了) Hook-->>Page: 再レンダリング → トリック結果UI Note over User,API: トリック終了 (phase=2) User->>Page: 次のトリックボタンクリック Page->>Hook: handleNextTrick() Hook->>API: gameExec("next") API-->>Hook: OhHellResponse (phase=1 or 3) Hook-->>Page: 再レンダリング → 次トリックUIまたはラウンド終了UI Note over User,API: ラウンド終了 (phase=3) User->>Page: 次のラウンドボタンクリック Page->>Hook: handleNextRound() Hook->>API: gameExec("nextround") API-->>Hook: OhHellResponse (phase=0 or 4) Hook-->>Page: 再レンダリング → 次ラウンドUIまたはゲーム終了UI ``` ### 2.7 BridgePage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as BridgePage participant Hook as useBridgeGame participant API as gameApi Note over User,API: オークションフェーズ (phase=0) User->>Page: ビッドレベル・スート選択 User->>Page: ビッドボタンクリック Page->>Hook: handleBid(bidType, bidLevel, bidSuit) Hook->>API: gameExec("bid", {bidType, bidLevel, bidSuit}) API-->>Hook: BridgeResponse (phase=1, コントラクト確定) Hook-->>Page: 再レンダリング → ダミー公開・プレイフェーズUI Note over User,API: プレイフェーズ (phase=1) User->>Page: 手札カードをクリック (またはダミーハンド操作) User->>Page: 出すボタンクリック Page->>Hook: handlePlay(cardIndex) Hook->>API: gameExec("play", {cardIndex}) API-->>Hook: BridgeResponse (phase=2, トリック完了) Hook-->>Page: 再レンダリング → トリック結果UI Note over User,API: トリック終了 (phase=2) User->>Page: 次のトリックボタンクリック Page->>Hook: handleNextTrick() Hook->>API: gameExec("next") API-->>Hook: BridgeResponse (phase=1 or 3) Hook-->>Page: 再レンダリング → 次トリックUIまたはラウンド終了UI Note over User,API: ラウンド終了 (phase=3) User->>Page: 次のラウンドボタンクリック Page->>Hook: handleNextRound() Hook->>API: gameExec("nextround") API-->>Hook: BridgeResponse (phase=0 or 4) Hook-->>Page: 再レンダリング → 次ラウンドUIまたはゲーム終了UI ``` ### 2.8 PineapplePage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as PineapplePage participant Hook as usePineappleGame participant API as gameApi Note over User,API: プリフロップ・フロップ (phase=1,2) User->>Page: ベッティングアクションボタンクリック Page->>Hook: handleBet/handleCall/handleFold/etc. Hook->>API: gameExec("bet", {amount}) API-->>Hook: PineappleResponse Hook-->>Page: 再レンダリング → ベッティングUI Note over User,API: ディスカードフェーズ (phase=3) User->>Page: 手札カードをクリック (捨てるカード選択) User->>Page: ディスカードボタンクリック Page->>Hook: handleDiscard(cardIdx) Hook->>API: gameExec("discard", {cardIdx}) API-->>Hook: PineappleResponse (phase=4, ターン) Hook-->>Page: 再レンダリング → 手札2枚 + ターンカード公開UI Note over User,API: ターン・リバー (phase=4,5) User->>Page: ベッティングアクションボタンクリック Page->>Hook: handleBet/handleCall/etc. Hook->>API: gameExec("bet", {amount}) API-->>Hook: PineappleResponse Hook-->>Page: 再レンダリング → ベッティングUI Note over User,API: ショーダウン・エンド (phase=6,7) Page->>Hook: 自動表示 Hook-->>Page: 再レンダリング → ショーダウン結果UI ``` ### 2.9 SpeedPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as SpeedPage participant Hook as useSpeedGame participant API as gameApi Note over User,API: プレイフェーズ (phase=0) User->>Page: 手札カードをクリックして選択 User->>Page: 場札をクリックしてカードを出す Page->>Hook: handlePlay(cardIndex, pileIndex) Hook->>API: gameExec("play", {cardIndex, pileIndex}) API-->>Hook: SpeedResponse (phase=0 or 1 or 2) Hook-->>Page: 再レンダリング → プレイUI Note over User,API: スタックフェーズ (phase=1) User->>Page: めくるボタンクリック Page->>Hook: handleFlip() Hook->>API: gameExec("flip") API-->>Hook: SpeedResponse (phase=0) Hook-->>Page: 再レンダリング → 新しい場札表示 Note over User,API: ゲーム終了 (phase=2) Page-->>User: 勝者表示 User->>Page: リセットボタンクリック Page->>Hook: handleReset() Hook->>API: gameExec("reset") API-->>Hook: SpeedResponse (phase=0) Hook-->>Page: 再レンダリング → プレイフェーズUI ``` ### 2.10 GoFishPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as GoFishPage participant Hook as useGoFishGame participant API as gameApi Note over User,API: プレイフェーズ (phase=0) - 要求 User->>Page: CPUプレイヤーをクリック (相手選択) User->>Page: ランクボタンをクリック (ランク選択) Page->>Hook: handleAsk(targetIdx, rank) Hook->>API: gameExec("ask", {target, rank}) API-->>Hook: GoFishResponse (phase=0 or 1) Hook-->>Page: 再レンダリング → 要求結果UI Note over User,API: CPU行動アニメーション Page-->>User: CPU要求をアニメーション再生 Note over User,API: ゲーム終了 (phase=1) Page-->>User: 勝者・ブック数表示 User->>Page: リセットボタンクリック Page->>Hook: handleReset() Hook->>API: gameExec("reset") API-->>Hook: GoFishResponse (phase=0) Hook-->>Page: 再レンダリング → プレイフェーズUI ``` ### 2.11 CanastaPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as CanastaPage participant Hook as useCanastaGame participant API as gameApi Note over User,API: ドローフェーズ (phase=0) User->>Page: 山札から引くボタンクリック Page->>Hook: handleDrawStock() Hook->>API: gameExec("drawstock") API-->>Hook: CanastaResponse (phase=1) Hook-->>Page: 再レンダリング → メルドフェーズUI Note over User,API: メルドフェーズ (phase=1) User->>Page: カード3枚以上選択 → メルドボタン Page->>Hook: handleMeldSelected() Hook->>API: gameExec("meld", {meldGroups}) API-->>Hook: CanastaResponse (phase=2) Hook-->>Page: 再レンダリング → ディスカードフェーズUI Note over User,API: ディスカードフェーズ (phase=2) User->>Page: カード1枚選択 → 捨てるボタン Page->>Hook: handleDiscard() Hook->>API: gameExec("discard", {cardIndex}) API-->>Hook: CanastaResponse (phase=0 or 3) Hook-->>Page: 再レンダリング → CPUターン後ドローフェーズUI Note over User,API: ラウンド終了 (phase=3) User->>Page: 次のラウンドボタンクリック Page->>Hook: handleNextRound() Hook->>API: gameExec("nextround") API-->>Hook: CanastaResponse (phase=0) Hook-->>Page: 再レンダリング → ドローフェーズUI ``` ### 2.12 PinochlePage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as PinochlePage participant Hook as usePinochleGame participant API as gameApi Note over User,API: ビッドフェーズ (phase=0) User->>Page: ビッド額入力 → ビッドボタン Page->>Hook: handleBid(amount) Hook->>API: gameExec("bid", {bidAmount}) API-->>Hook: PinochleResponse (phase=0 or 1) Hook-->>Page: 再レンダリング → CPUビッド後 Note over User,API: トランプ宣言フェーズ (phase=1) User->>Page: スートボタンクリック Page->>Hook: handleCallTrump(suit) Hook->>API: gameExec("trump", {suit}) API-->>Hook: PinochleResponse (phase=2) Hook-->>Page: 再レンダリング → メルドフェーズUI Note over User,API: メルドフェーズ (phase=2) User->>Page: メルド確認ボタン Page->>Hook: handleConfirmMelds() Hook->>API: gameExec("meld") API-->>Hook: PinochleResponse (phase=3) Hook-->>Page: 再レンダリング → プレイフェーズUI Note over User,API: プレイフェーズ (phase=3) User->>Page: カードクリック Page->>Hook: handlePlay(cardIndex) Hook->>API: gameExec("play", {cardIndex}) API-->>Hook: PinochleResponse (phase=3 or 4) Hook-->>Page: 再レンダリング → CPUプレイ後 Note over User,API: トリック終了 (phase=4) User->>Page: 次のトリックボタン Page->>Hook: handleNextTrick() Hook->>API: gameExec("next") API-->>Hook: PinochleResponse (phase=3 or 5) Hook-->>Page: 再レンダリング Note over User,API: ラウンド終了 (phase=5) User->>Page: 次のラウンドボタン Page->>Hook: handleNextRound() Hook->>API: gameExec("nextround") API-->>Hook: PinochleResponse (phase=0) Hook-->>Page: 再レンダリング → ビッドフェーズUI ``` ### 2.13 PigsTailPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as PigsTailPage participant Hook as usePigsTailGame participant API as gameApi Note over User,API: プレイフェーズ (phase=0) - ドロー User->>Page: 引くボタンクリック Page->>Hook: handleDraw() Hook->>API: gameExec("draw") API-->>Hook: PigsTailResponse (phase=0 or 1) Hook-->>Page: 再レンダリング → ドロー結果UI Note over User,API: CPU行動アニメーション Page-->>User: CPUドローをアニメーション再生 Note over User,API: ゲーム終了 (phase=1) Page-->>User: 勝者・手札枚数表示 User->>Page: リセットボタンクリック Page->>Hook: handleReset() Hook->>API: gameExec("reset") API-->>Hook: PigsTailResponse (phase=0) Hook-->>Page: 再レンダリング → プレイフェーズUI ``` ### 2.14 SevenCardStudPage フェーズ別レンダリングフロー ```mermaid sequenceDiagram participant User as ユーザー participant Page as SevenCardStudPage participant Hook as useSevenCardStudGame participant API as gameApi Note over User,API: サード~セブンスストリート - ベッティング User->>Page: アクションボタンクリック (fold/check/call/bet/raise/allin) Page->>Hook: handleAction(command, amount) Hook->>API: gameExec(command, {amount, humanPlayMs}) API-->>Hook: SevenCardStudResponse (phase=1-5) Hook-->>Page: 再レンダリング → ベッティング結果UI Note over User,API: CPU行動アニメーション Page-->>User: CPUアクションをアニメーション再生 Note over User,API: ショーダウン (phase=6) Page-->>User: 全員の手札・役名表示 Note over User,API: マック/ショー選択 User->>Page: マック or ショーボタンクリック Page->>Hook: handleMuck() / handleShow() Hook->>API: gameExec("muck" / "show") Note over User,API: リバイ/アドオン (phase=8) User->>Page: リバイ or スキップボタンクリック Page->>Hook: handleRebuy() / handleSkipRebuy() Hook->>API: gameExec("rebuy" / "skiprebuy") API-->>Hook: SevenCardStudResponse (phase=0) Hook-->>Page: 再レンダリング → 次ハンドUI ``` ### 2.15 CLIモード コマンド実行フロー ```mermaid sequenceDiagram participant User as ユーザー (CliTerminal入力) participant useCliGame as useCliGame participant Parser as parseXxxCommand participant API as useGameApi participant Formatter as formatXxxState participant Log as useCliMode.logEntries User->>useCliGame: handleCommand("hit") useCliGame->>Log: addInput("hit") useCliGame->>Parser: parseCommand("hit") Parser-->>useCliGame: { args = ["hit"] } useCliGame->>API: 実行("hit") API-->>useCliGame: state更新 useCliGame->>Formatter: formatResponse(state) Formatter-->>useCliGame: テキスト出力 useCliGame->>Log: addOutput(テキスト) Log-->>User: CliTerminal再レンダリング (自動スクロール) ``` --- ## 3. ステートマシン図 ### 3.1 ゲームページ表示状態 ```mermaid stateDiagram-v2 [*] --> Loading : ページマウント Loading --> Playing : Reset API 成功 Loading --> Error : API エラー state Playing { [*] --> WaitingInput : 自分のターン WaitingInput --> Processing : アクション実行 Processing --> CpuReplay : CPUアクションあり Processing --> WaitingInput : 即座に次のターン CpuReplay --> WaitingInput : リプレイ完了 Processing --> PhaseTransition : フェーズ変化 PhaseTransition --> WaitingInput : 次フェーズのUI表示 } Playing --> GameEnd : ゲーム終了フェーズ Playing --> Error : API エラー Error --> Loading : リトライ GameEnd --> Loading : リセット GameEnd --> [*] : ページ離脱 ``` ### 3.2 カード選択状態 (useCardSelection) ```mermaid stateDiagram-v2 [*] --> Empty : 初期化 Empty --> Selected : toggle(cardIndex) Selected --> Selected : toggle(別のcardIndex) → 追加/解除 Selected --> Empty : clear() Selected --> Empty : confirm() → アクション実行後クリア note right of Selected : selected = number[]\n選択中のカードインデックス配列 note right of Empty : selected = []\n何も選択されていない ``` ### 3.3 確認ダイアログ状態 (useConfirmDialog) ```mermaid stateDiagram-v2 [*] --> Closed : 初期化 Closed --> Open : requestConfirm(callback) Open --> Closed : cancel() / Escapeキー Open --> Closed : confirm() → callback実行 note right of Open : フォーカストラップ有効\nTab キーでダイアログ内循環\nEscape で閉じる ``` ### 3.4 アクションログ状態 (useActionLog) ```mermaid stateDiagram-v2 [*] --> Hidden : 初期化 Hidden --> Fetching : show() Fetching --> Visible : API成功 (entries取得) Fetching --> Hidden : APIエラー Visible --> Hidden : hide() Visible --> Fetching : show() (再取得) ``` ### 3.5 ゲーム設定パネル状態 ```mermaid stateDiagram-v2 [*] --> Collapsed : 初期化 Collapsed --> Expanded : パネル開く Expanded --> Collapsed : パネル閉じる state Expanded { [*] --> Idle Idle --> Changing : 設定変更 Changing --> Resetting : Reset API呼出し Resetting --> Idle : 新しい設定でゲーム再開 } note right of Expanded : 設定変更時は自動的にゲームリセット\nhandleConfigChange → exec reset with newConfig ``` ### 3.6 チュートリアル状態 (useTutorial) ```mermaid stateDiagram-v2 [*] --> Inactive : 初期化 Inactive --> Active : start() Inactive --> Inactive : next()/skip() (何もしない) state Active { [*] --> Step_0 : onEnter呼出し Step_0 --> Step_N : next() / onEnter呼出し Step_N --> Step_N : next() (次ステップ) } Active --> Completed : next() (最終ステップ) Active --> Inactive : skip() Completed --> Active : start() (再開) note right of Completed : localStorage に完了フラグ保存\ntutorial_completed_{gameName} = true note right of Active : advanceOn=click → 対象クリックで next()\nadvanceOn=next → 次へボタンで next() ``` ### 3.7 CLIモード状態 (useCliMode + useCliGame) ```mermaid stateDiagram-v2 [*] --> GUI : 初期化 (localStorage確認) GUI --> CLI : toggleCli() CLI --> GUI : toggleCli() state CLI { [*] --> Idle : CliTerminal表示 Idle --> Parsing : コマンド入力 (Enter) Parsing --> Help : help/? Parsing --> Clear : clear Parsing --> Executing : コマンドパース成功 Parsing --> Error : パースエラー Help --> Idle : ヘルプテキスト表示 Clear --> Idle : ログクリア Error --> Idle : エラーメッセージ表示 Executing --> Formatting : API成功 Executing --> Error : APIエラー Formatting --> Idle : 整形テキスト表示 (自動スクロール) } note right of GUI : GUIコンポーネント表示\nGameFooter + カードUI note right of CLI : CliTerminal表示\n黒背景 + 等幅フォント\nコマンド履歴 (up/down) note left of GUI : ゲーム状態はGUI/CLI共有\nuseGameApi.state は同一 ```
# バカラ(CUI版)遊び方 ## ゲーム概要 カジノの定番カードゲームです。プレイヤーとバンカーのどちらが勝つかを予想してベットします。カードの合計値の下一桁が9に近い方が勝ちです。 ## 起動方法 ```sh go run ./cmd/cli baccarat ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - カードの点数: A=1, 2〜9=額面通り, 10/J/Q/K=0 - ハンドの値は各カードの点数の合計の下一桁 - プレイヤーとバンカーにそれぞれ2枚ずつ配る - 第3カードルール(サードカードルール)に従って追加カードが配られることがある ### ベットタイプと配当 | ベット先 | 条件 | 配当 | |---------|------|------| | プレイヤー (0) | プレイヤーの勝ち | 2倍 | | バンカー (1) | バンカーの勝ち | 1.95倍(5%コミッション) | | タイ (2) | 引き分け | 9倍(8:1) | ### ナチュラル - 最初の2枚の合計が8または9の場合「ナチュラル」となり、第3カードは配られない ### 第3カードルール **プレイヤー側:** - 合計0〜5: 1枚引く - 合計6〜7: スタンド **バンカー側:**(プレイヤーが第3カードを引いた場合) - 合計0〜2: 必ず引く - 合計3: プレイヤーの第3カードが8以外なら引く - 合計4: プレイヤーの第3カードが2〜7なら引く - 合計5: プレイヤーの第3カードが4〜7なら引く - 合計6: プレイヤーの第3カードが6〜7なら引く - 合計7: スタンド ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ベットフェーズ] B --> C[b ベット額 ベットタイプ] C --> D[カードを配る] D --> E{第3カードルール} E -->|条件を満たす| F[第3カードを配る] E -->|条件を満たさない| G[勝敗判定] F --> G G --> H[結果表示] H -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `bet ` | `b ` | ベットする(金額とタイプを指定) | | `log` | `l` | アクションログ(棋譜)を表示 | | `clearhistory` | `ch` | Big Road(出目履歴)をクリア | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `r` → ゲームリセット - `b 100 0` → 100チップをプレイヤーにベット - `b 200 1` → 200チップをバンカーにベット - `b 50 2` → 50チップをタイにベット - `l` → 棋譜を表示 - `ch` → Big Road(出目履歴)をクリア ## 画面の見方 ``` ========== バカラ ========== チップ: 1000 ---------- ベットフェーズ b・・・ベット l・・・棋譜 ========== > b 100 0 ========== バカラ ========== チップ: 1100 ---------- プレイヤー (値: 9): ♠9 ♥K バンカー (値: 7): ♣5 ♦2 ---------- プレイヤーの勝ち! 配当: 200 ---------- r・・・リセット l・・・棋譜 ========== ``` - `チップ: N`: 所持チップ数 - `プレイヤー (値: N)`: プレイヤーのハンドと合計値 - `バンカー (値: N)`: バンカーのハンドと合計値 - `配当: N`: 獲得配当 ## 遊び方のコツ - バンカーベットの方が統計的にわずかに有利です(ハウスエッジ約1.06%) - プレイヤーベットのハウスエッジは約1.24% - タイベットはハウスエッジが高い(約14.4%)ので控えめに - ベット額の管理が重要です。一度に大きくベットせず、計画的にプレイしましょう # ブラックジャック(CUI版)遊び方 ## ゲーム概要 ディーラーとプレイヤーが1対1で対戦するカードゲームです。手札の合計値を21に近づけ、21を超えずにディーラーより高い点数を目指します。 ## 起動方法 ```sh go run ./cmd/cli blackjack ``` ## ルール ### カードの点数 | カード | 点数 | |--------|------| | 2〜10 | 数字どおり | | J, Q, K | 10 | | A | 11(合計が21を超える場合は1) | ### チップシステム - 初期チップ: 1000 - 最低ベット: 10(10の倍数で指定) - チップが10未満になると自動的に1000にリセットされます ### 勝敗判定 - プレイヤーの手札がディーラーより21に近ければ勝ち(賭け金と同額を獲得) - ナチュラルブラックジャック(最初の2枚で21)の場合、3:2の配当 - バスト(21超過)した場合は即負け - 引き分けの場合、賭け金はそのまま返却 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ベットフェーズ] B -->|bet N| C[カード配布] C --> ES{アーリーサレンダー有効?} ES -->|はい| ESF[アーリーサレンダーフェーズ] ESF -->|earlysurrender| O ESF -->|declineearlysurrender| D ES -->|いいえ| D{ディーラーの表札がA?} D -->|はい| E[インシュランスフェーズ] D -->|いいえ| F[アクションフェーズ] E -->|insurance / declineinsurance| F F --> G{プレイヤーの選択} G -->|hit| H[カードを1枚引く] H --> I{バスト?} I -->|はい| J[負け] I -->|いいえ| G G -->|stand| K[ディーラーのターン] G -->|doubledown| L[ベット2倍 + 1枚引いてスタンド] L --> K G -->|split| M[手札を分割して続行] M --> G G -->|surrender| O[半額返却して降りる] O --> K K --> N[結果表示] J --> N N -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいラウンドを開始 | | `bet N [PP] [T3] [HC]` | `b N [PP] [T3] [HC]` | N チップをベット。任意でサイドベット額とハンド数を指定(例: `b 100 10 20 2`) | | `hit` | `h` | カードを1枚引く | | `stand` | `s` | カードを引かずにターン終了 | | `doubledown` | `d` | ベットを2倍にし、1枚だけ引いてスタンド | | `split` | `sp` | 同じ点数の2枚の手札を分割(最大4ハンド) | | `insurance` | `i` | インシュランスを購入(ベットの半額) | | `declineinsurance` | `di` | インシュランスを辞退 | | `surrender` | `sur` | サレンダー(最初の2枚のみ): ベットの半額を返却してゲームを降りる | | `togglehint` | `hint` | ベーシックストラテジーヒントのON/OFFを切り替える | | `setdeckcount N` | `sd N` | シューのデッキ数を設定(有効値: 1/2/4/6/8)。BETフェーズのみ有効 | | `togglesoft17` | `soft17` | ディーラーのソフト17ルールを切り替える(H17: ソフト17でヒット / S17: ソフト17でスタンド) | | `togglecounting` | `counting` | カードカウンティング表示のON/OFFを切り替える | | `setcountingsystem N` | `scs N` | カウンティングシステムを設定(0=Hi-Lo / 1=KO / 2=Zen Count / 3=Omega II) | | `toggledas` | `das` | スプリット後のダブルダウン(DAS)許可のON/OFFを切り替える | | `setpenetration N` | `pen N` | デッキペネトレーション率を設定(有効値: 50 / 75)。BETフェーズのみ有効 | | `setcpucount N` | `scc N` | CPUプレイヤー数を設定(0〜3)。BETフェーズのみ有効 | | `earlysurrender` | `es` | アーリーサレンダー(ディーラーのBJ確認前にサレンダー) | | `declineearlysurrender` | `des` | アーリーサレンダーを辞退して通常プレイを続行 | | `setsurrenderrule N` | `ssr N` | サレンダールールを設定(0=レイトサレンダー / 1=アーリーサレンダー / 2=サレンダー禁止) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ## 特殊ルール ### インシュランス ディーラーの表向きカードがAの場合に提示されます。ベットの半額を支払い、ディーラーがブラックジャックなら2:1の配当を受け取ります。 ### ダブルダウン 最初の2枚の状態でのみ選択可能です。ベットが2倍になり、カードを1枚だけ引いて自動的にスタンドします。 ### スプリット 最初の2枚が同じブラックジャック点数(例: K と 10)の場合に選択可能です。手札を2つに分割し、それぞれに元のベットと同額が賭けられます。シングルハンド時は最大4ハンド、マルチハンド時は合計最大8ハンドまで分割できます。Aをスプリットした場合、各手札に1枚ずつ配られて自動スタンドとなります。 ### サレンダー アクションフェーズの最初の2枚の状態でのみ選択可能です。`surrender`(短縮形: `sur`)を入力するとベットの半額が返却され、そのハンドは終了します。 ### サレンダールール設定 `setsurrenderrule N`(短縮形: `ssr N`)でサレンダールールを変更できます。 | 値 | ルール | 説明 | |----|--------|------| | 0 | レイトサレンダー(デフォルト) | ディーラーのBJ確認後にサレンダー可能 | | 1 | アーリーサレンダー | ディーラーのBJ確認前にサレンダー可能(アーリーサレンダーフェーズが追加) | | 2 | サレンダー禁止 | サレンダー不可 | アーリーサレンダーが有効な場合、カード配布後にアーリーサレンダーフェーズ(`EARLY_SURRENDER`)が追加されます。このフェーズで `earlysurrender`(短縮形: `es`)を入力するとベットの半額が返却されゲームを降りることができます。`declineearlysurrender`(短縮形: `des`)を入力すると通常のプレイを続行します。設定はセッション内で維持されます。 ### ベーシックストラテジーヒント `togglehint`(短縮形: `hint`)でON/OFFを切り替えます。ONにすると、現在のハンドとディーラーのアップカードに基づいた最適アクションが `[HINT: ○○]` として表示されます。ヒントはセッション内で維持されます(`reset` してもON/OFFは変わりません)。 ### デッキ数(シュー)設定 BETフェーズ中に `setdeckcount N`(短縮形: `sd N`)でシューのデッキ数を変更できます。有効な値は 1 / 2 / 4 / 6 / 8 です。次のラウンドの `reset` 時に新しいシューが使用されます。 ### ソフト17ルールトグル `togglesoft17`(短縮形: `soft17`)でディーラーのソフト17ルールを切り替えます。H17(ヒット)とS17(スタンド)を交互に切り替えます。H17ではディーラーはソフト17(Aを11と数えて合計17)でもう1枚引きます。S17ではソフト17でスタンドします。設定はセッション内で維持されます。 ### カードカウンティング練習 `togglecounting`(短縮形: `counting`)でカードカウンティング表示のON/OFFを切り替えます。ONにするとランニングカウント(RC)とトゥルーカウント(TC)が表示されます。カウンティング練習モードとして、実際のカード配布に応じてカウントが更新されます。 `setcountingsystem N`(短縮形: `scs N`)でカウンティングシステムを変更できます。 | 値 | システム | タイプ | |----|----------|--------| | 0 | Hi-Lo | バランス型(TC表示あり) | | 1 | KO | アンバランス型(TC=N/A) | | 2 | Zen Count | バランス型(TC表示あり) | | 3 | Omega II | バランス型(TC表示あり) | KOシステムはアンバランス型のため、トゥルーカウント(TC)は表示されず `TC=N/A` となります。 ### DAS(スプリット後のダブルダウン) `toggledas`(短縮形: `das`)でスプリット後のダブルダウン(Double After Split)の許可/禁止を切り替えます。デフォルトはON(DAS許可)です。OFFにすると、スプリットで分割されたハンドではダブルダウンが選択できなくなります。設定はセッション内で維持されます。 ### デッキペネトレーション設定 `setpenetration N`(短縮形: `pen N`)でデッキペネトレーション率(%)を設定します。有効な値は 50 / 75 です。デフォルトは75%です。75%ではカードの75%が使用された時点でシューが再構築されます。50%ではカードの50%が使用された時点で再構築されます。設定はセッション内で維持されます。 ### マルチプレイヤーCPU席 `setcpucount N`(短縮形: `scc N`)で0〜3名のCPUプレイヤーを追加できます。BETフェーズ中に設定を変更すると、次の `reset` 時に反映されます。CPUプレイヤーはベーシックストラテジーに基づいて自動的にプレイします。 カウンティングが有効な場合、CPUプレイヤーはカードカウンティングAIを使用します: - **ベットスプレッド**: カウント値に応じてベット額を変動させます(基本50チップ) | カウント | 倍率 | ベット額 | |----------|------|----------| | <= 1 | 1x | 50 | | 2 | 2x | 100 | | 3 | 4x | 200 | | 4 | 8x | 400 | | >= 5 | 16x | 800 | - **インシュランス判断**: カウント >= 3 のときインシュランスを取ります - バランス型システム(Hi-Lo/Zen/Omega II)ではトゥルーカウント、KOではランニングカウントを使用します - ベット額は所持チップを上限とし、BJMinBet(10)の倍数に丸められます ### サイドベット メインベットと同時にサイドベットを賭けることができます。`bet N PP T3 HC` の形式で、PP(Perfect Pairs)、T3(21+3)のベット額、HC(ハンド数)を指定します(例: `b 100 10 20 2`)。省略した場合はそれぞれ0(賭けない)、1ハンドとして扱われます。サイドベットはカード配布直後に評価され、メインハンドとは独立に精算されます。サイドベットはハンド0のみに適用されます。 #### Perfect Pairs(プレイヤーの最初の2枚) | 結果 | 条件 | 配当 | |------|------|------| | Perfect Pair | 同じスート・同じ値 | 25:1 | | Colored Pair | 同じ色・同じ値 | 12:1 | | Mixed Pair | 異なる色・同じ値 | 6:1 | #### 21+3(プレイヤーの2枚+ディーラーのアップカード) | 結果 | 条件 | 配当 | |------|------|------| | Suited Trips | 同じスート・同じ値 | 100:1 | | Straight Flush | 同じスート・連続値 | 40:1 | | Three of a Kind | 同じ値・異なるスート | 30:1 | | Straight | 連続値 | 10:1 | | Flush | 同じスート | 5:1 | ### マルチハンド `bet N PP T3 HC` の4番目の引数でハンド数(1〜3)を指定できます(例: `b 100 0 0 2` で2ハンド)。省略時は1ハンドです。各ハンドに同額のベットが賭けられ、カードはインターリーブ方式(ハンド0-カード1、ハンド1-カード1、...、ディーラー-カード1、...)で配られます。 - 総コスト: `ベット額 × ハンド数 + サイドベット額` - サイドベットはハンド0のみに適用 - インシュランスはハンド0のベット額に基づいて1回のみ - ナチュラルBJ: ディーラーがBJなら即終了、全プレイヤーハンドがBJなら即終了、一部のみBJの場合はBJハンドを自動スタンドして続行 - スプリット上限: マルチハンド時は合計最大8ハンド - マルチハンドで配られた初期ハンドでのBJは3:2配当(スプリットで生じたBJは1:1配当) - CUI出力に `multi-hand: Nハンド` が表示されます(2ハンド以上の場合) ## 画面の見方 ``` ---------- chips: player=950 dealer=1000 decks=1 soft17=S17 count (Hi-Lo): RC=+3 TC=+1.5 phase: ACTION dealer score SPADE 10, ---------- cpu1 score 18 bet=50 chips=900 HEART 10,DIAMOND 8 ---------- player (*) score 15 bet=50 HEART 8,DIAMOND 7 [HINT: HIT] ---------- ``` - `chips`: プレイヤーとディーラーの所持チップ、デッキ数、ソフト17ルール - `count (システム名): RC=N TC=N`: ランニングカウント / トゥルーカウント(カウンティングON時のみ、KOシステムでは `TC=N/A`) - `phase`: 現在のフェーズ(BET / INSURANCE / ACTION / END / EARLY_SURRENDER) - `dealer score`: ディーラーの手札(ゲーム中は裏札が非表示) - `cpuN score N bet=M chips=C`: CPUプレイヤーの手札情報(CPUプレイヤーがいる場合のみ) - `player (*) score N bet=M`: プレイヤーの手札情報(`(*)` はアクティブなハンド) - ステータスタグ: `[DD]` ダブルダウン / `[BUST]` バスト / `[STAND]` スタンド / `[BJ]` ナチュラルBJ / `[SURRENDER]` サレンダー - `[HINT: ○○]`: ベーシックストラテジーによる推奨アクション(ヒントON時のみ) # コントラクトブリッジ(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶ2チーム制(0+2 vs 1+3)のトリックテイキング型カードゲームです。標準52枚のカード(各プレイヤー13枚)を使用し、オークション(ビッド)でコントラクトを決定した後、13トリックをプレイします。ラバーブリッジ方式のスコアリングで、先に2ゲームを獲得したチームがラバーに勝利します。 ## 起動方法 ```sh go run ./cmd/trumpcards bridge go run ./cmd/trumpcards --lang en bridge # 英語モード ``` ## ルール ### 基本ルール - 52枚のカードを4人に均等配布(1人13枚) - 4人を2チームに分ける(プレイヤー0+2 vs プレイヤー1+3) - オークション(ビッド)でコントラクト(レベル+スート/ノートランプ)を決定 - コントラクトを勝ち取ったプレイヤーがディクレアラー(宣言者) - ディクレアラーのパートナーがダミー(手札を公開し、ディクレアラーが操作) - リードスートに従う(フォロースート) - 切り札(トランプ)がある場合、切り札が勝つ ### オークション(ビッド) - ディーラーの左隣から時計回りにビッド - ビッドはレベル(1〜7)とスート(クラブ < ダイヤ < ハート < スペード < ノートランプ)の組み合わせ - 前のビッドより高いビッドのみ可能 - **パス**: ビッドを見送る - **ダブル**: 相手チームの最後のビッドに対して宣言(得点/失点が倍) - **リダブル**: 自チームへのダブルに対して宣言(得点/失点が4倍) - 3人連続パスでオークション終了、最後のビッドがコントラクトになる - 4人全員パスの場合はパスアウト(ラウンドやり直し) ### プレイ - ディクレアラーの左隣がオープニングリード - リード後、ダミーの手札が公開される - ディクレアラーはダミーのカードも操作する - フォロースート必須(持っていない場合のみ他のスート可) - トリックの勝者が次のリードを行う ### 得点(ラバーブリッジ) #### ライン以下(コントラクトポイント) - **クラブ/ダイヤ(マイナースート)**: 1トリックあたり20点 - **ハート/スペード(メジャースート)**: 1トリックあたり30点 - **ノートランプ**: 最初のトリック40点、以降30点 - ダブル時は2倍、リダブル時は4倍 - ライン以下が100点以上でゲーム獲得 #### ライン以上(ボーナス/ペナルティ) - **オーバートリック**: コントラクト以上に取ったトリック分のボーナス - **アンダートリック**: コントラクト未達のペナルティ(バルネラブルで増加) - **スラムボーナス**: スモールスラム(6レベル達成) 500/750、グランドスラム(7レベル達成) 1000/1500 - **ラバーボーナス**: 2-0勝ち 700点、2-1勝ち 500点 #### バルネラビリティ - 1ゲーム獲得したチームはバルネラブル(脆弱)状態になる - バルネラブル時はアンダートリックのペナルティとスラムボーナスが増加 ### ゲーム終了 - **勝利条件**: 先に2ゲームを獲得してラバーに勝利 ### CPU難易度 - **Easy** (0): ランダムにビッド・プレイ - **Normal** (1): HCP(ハイカードポイント)に基づくビッド、基本的なトリック戦略 - **Hard** (2): HCP+分布点によるビッド、パートナー連携、切り札管理、ボイド戦略 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各13枚] B --> C[オークション開始] C --> D{ビッド?} D -->|ビッド| E[レベル+スートを宣言] D -->|パス| F{3連続パス?} D -->|ダブル/リダブル| E F -->|いいえ| D E --> F F -->|はい| G{コントラクト確定?} G -->|はい| H[ダミー公開・プレイ開始] G -->|4人全員パス| B H --> I{プレイヤーの手番?} I -->|はい| J[カードを選んで出す] I -->|いいえ| K[CPU/ダミーが自動でカードを出す] J --> L[トリック完了] K --> L L --> M{13トリック終了?} M -->|いいえ| I M -->|はい| N[ラウンド終了 - 得点計算] N --> O{ラバー完了?} O -->|いいえ| B O -->|はい| P[ゲーム終了 - 結果表示] P -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `bid [level] [suit]` | `b [level] [suit]` | ビッド(type: 0=パス, 1=通常, 2=ダブル, 3=リダブル) | | `play ` | `p ` | カードをプレイ(インデックス指定) | | `next` | `n` | 次のトリックへ | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `hint` | `h` | 推奨ビッド/カードのヒントを表示 | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `b 0` → パス - `b 1 1 5` → 1ノートランプをビッド(レベル1, スート5=ノートランプ) - `b 1 3 4` → 3スペードをビッド(レベル3, スート4=スペード) - `b 2` → ダブル - `b 3` → リダブル - `p 0` → インデックス0のカードを出す ### スートコード | コード | スート | |--------|--------| | 1 | クラブ | | 2 | ダイヤ | | 3 | ハート | | 4 | スペード | | 5 | ノートランプ | ## 画面の見方 ``` ========== Contract Bridge (コントラクトブリッジ) ========== ラウンド 2 / トリック 5 コントラクト: 3NT by CPU 1 (ダブル) ディーラー: CPU 2 チーム1(あなた+CPU 2): ゲーム 1, Below 60, Above 50 チーム2(CPU 1+CPU 3): ゲーム 0, Below 40, Above 0 [V] ---------- [You]: 8枚 [0]HEART A [1]HEART K [2]SPADE Q [3]DIAMOND 10 [4]CLOVER 7 ... CPU 1 (ディクレアラー): 8枚 CPU 2 (ダミー): 8枚 [0]SPADE A [1]SPADE 10 [2]HEART 5 [3]DIAMOND 3 ... CPU 3: 8枚 ---------- 現在のトリック: CPU 1: SPADE K 手番: あなた p ・・・カードを出す ========== ``` - `ラウンド N / トリック M`: 現在のラウンドとトリック番号 - `コントラクト`: 確定したコントラクト(レベル+スート、ディクレアラー、ダブル状態) - `ディーラー`: 現在のディーラー - `チームN`: 各チームのゲーム数、ライン以下・以上のスコア - `[V]`: バルネラブル状態の表示 - `[0]HEART A`...: インデックス付きの手札カード - `(ダミー)`: ダミーの手札は公開表示 - `現在のトリック`: トリックに出されたカード ## 遊び方のコツ - HCP(A=4, K=3, Q=2, J=1)が13点以上あればオープニングビッドを検討しましょう - パートナーとのビッドのやり取りで互いの手札の強さを伝え合います - ディクレアラーの場合、ダミーの手札と合わせて作戦を立てましょう - フォロースートできない場合、切り札で取るか、不要なカードを捨てるか判断しましょう - バルネラブル時はアンダートリックのペナルティが大きいため、無理なビッドは避けましょう - 迷った時は `h` コマンドでヒントを表示できます # カナスタ(CUI版)遊び方 ## ゲーム概要 カナスタは2人対戦のラミー系カードゲームです。2デッキ+4枚のジョーカー(計108枚)を使用し、同ランクのカードを集めてメルド(3枚以上の組)を作り、7枚以上のメルド「カナスタ」の完成を目指します。先に目標スコア(デフォルト5000点)に到達したプレイヤーが勝利します。 ## 起動方法 ```sh go run ./cmd/trumpcards canasta go run ./cmd/trumpcards --lang en canasta ``` ## ルール ### 基本ルール - **デッキ**: 標準52枚×2デッキ+ジョーカー4枚=108枚 - **配布**: 各プレイヤーに15枚 - **ワイルドカード**: 2とジョーカーはワイルド - **赤3**: ハート3・ダイヤ3は引いた時点で自動的に場に出し、山札から補充 - **黒3**: スペード3・クローバー3は捨てるだけ(メルド不可、相手のピックアップをブロック) ### メルド - 同ランク3枚以上で構成 - ナチュラルカード最低2枚必要 - ワイルドカードは最大3枚まで - カナスタ: 7枚以上のメルド(ナチュラル=ワイルドなし、ミックス=ワイルドあり) ### 初回メルド最低点 累積スコアに応じて初回メルドの最低合計点が変わります: | 累積スコア | 最低点 | |-----------|--------| | マイナス | 15点 | | 0〜1499 | 50点 | | 1500〜2999 | 90点 | | 3000以上 | 120点 | ### 捨て札の山を取る - トップカードと同ランクのナチュラルカード2枚(ペア)が必要 - 山全体を手札に加える - フリーズ状態(ワイルドが捨てられた場合)でも同条件 ### 得点 | カード | 点数 | |--------|------| | ジョーカー | 50点 | | 2、A | 20点 | | K〜8 | 10点 | | 7〜4 | 5点 | | 黒3 | 5点 | | ボーナス | 点数 | |----------|------| | ナチュラルカナスタ | 500点 | | ミックスカナスタ | 300点 | | 上がりボーナス | 100点 | | コンシールド上がり | 200点 | | 赤3(1枚あたり) | 100点 | | 赤3(4枚全部) | 800点 | ### CPU難易度 - Easy: ランダムプレイ - Normal: 基本戦略(ペアがあれば山を取る) - Hard: 戦略的プレイ(山のサイズを考慮) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ドローフェーズ] B --> C{山札から引く or 捨て札を取る} C -->|ds| D[メルドフェーズ] C -->|dd idx,idx| D D --> E{メルドする or スキップ} E -->|m idx,idx,idx| F[ディスカードフェーズ] E -->|sm| F F --> G{カードを捨てる or 上がる} G -->|d idx| B G -->|go| H[ラウンド終了] H -->|nr| B H --> I{目標スコア到達?} I -->|Yes| J[ゲーム終了] I -->|No| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | reset | r | ゲームをリセット | | drawstock | ds | 山札から引く | | drawdiscard idx,idx | dd idx,idx | 捨て札の山を取る(ナチュラルペアのインデックス) | | meld idx,idx,idx;idx,idx,idx | m idx,...;idx,... | メルドを出す(;でグループ区切り) | | skipmeld | sm | メルドせずにスキップ | | discard idx | d idx | カードを捨てる | | goout | go | 上がる(カナスタが必要) | | nextround | nr | 次のラウンドへ | | setdifficulty n | sd n | CPU難易度変更 (0=Easy, 1=Normal, 2=Hard) | | setlimit n | sl n | 目標スコア変更 | | log | l | 棋譜を表示 | | quit | q | 終了 | | help | ? | ヘルプを表示 | ## 画面の見方 ``` ============================== Canasta (カナスタ) ============================== ラウンド: 1 山札: 67枚 捨て札: 1枚 捨て札: ♠7 You: 累積0点 ラウンド0点 15枚 メルド[ナチュラル]: ♠K, ♥K, ♦K [0]♣2 [1]♠4 [2]♥5 [3]♦6 [4]♣7 ... CPU 1: 累積0点 ラウンド0点 15枚 ---------- 手番: You (ドローフェーズ) ds・・・山札から引く dd ・・・捨て札の山を取る ============================== ``` ## 遊び方のコツ - カナスタ(7枚以上のメルド)の完成を最優先にしましょう - ナチュラルカナスタ(ワイルドなし)は500点のボーナス - 捨て札の山が大きい時は積極的に取りましょう - ワイルドカードを捨てると山がフリーズするので注意 - 黒3を捨てると相手の山取りをブロックできます - 赤3は自動的に場に出るので手札管理に注意 # クロックソリティア(CUI版)遊び方 ## ゲーム概要 1人用の完全自動ソリティアカードゲームです。52枚のカードを時計の文字盤に見立てた13箇所(1〜12時の位置+中央のK置き場)に4枚ずつ伏せて配り、カードを1枚ずつ表向きにして対応する位置に移動させます。4枚目のKが表向きになる前に全カードを表向きにするとクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards clocksolitaire go run ./cmd/trumpcards --lang en clocksolitaire # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 13箇所の山に4枚ずつ伏せて配る - 位置1〜12 = 時計の1時〜12時の位置 - 位置13(中央)= Kの置き場 - ゲームは完全自動で進行(シャッフル後にプレイヤーの選択はなし) ### カードの移動ルール - 現在の山のトップカードを表向きにし、そのランクに対応する山の一番下へ移動させる - A = 位置1(1時) - 2〜10 = 位置2〜10 - J = 位置11(11時) - Q = 位置12(12時) - K = 中央(位置13) - 移動先の山が次の「現在の山」になる ### ゲームクリア - 4枚目のKが表向きになる前に全52枚が表向きになるとクリア ### ゲームオーバー - 4枚目のKが表向きになった時点で、まだ伏せカードが残っていればゲームオーバー - Kは全て中央の山に集まるため、4枚目のKが出ると中央以外への移動ができなくなる ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[52枚を13山に4枚ずつ伏せて配る] B --> C[現在の山のトップを表向きにする] C --> D{ランクに対応する山へ移動} D --> E{全カードが表向き?} E -->|はい| F[ゲームクリア] E -->|いいえ| G{4枚目のKが表向きになった?} G -->|いいえ| C G -->|はい| H{伏せカードが残っている?} H -->|いいえ| F H -->|はい| I[ゲームオーバー] F -->|reset| A I -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `step` | `s` | カードを1枚表向きにして対応する山へ移動 | | `autoplay` | `a` | 残りのカードをすべて自動で処理してゲームを完了させる | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `r` → 新しいゲームを開始する - `s` → カードを1枚進める - `a` → 残りすべてを一気に処理する - `l` → これまでの手順を確認する ## 画面の見方 ``` ========== Clock Solitaire (クロックソリティア) ========== [ 1] ♠A(表) ■ ■ ■ [ 2] ■ ■ ■ ■ [ 3] ■ ■ ■ ■ [ 4] ■ ■ ■ ■ [ 5] ■ ■ ■ ■ [ 6] ■ ■ ■ ■ [ 7] ■ ■ ■ ■ [ 8] ♠8(表) ■ ■ ■ [ 9] ■ ■ ■ ■ [ 10] ■ ■ ■ ■ [ 11] ■ ■ ■ ■ [ 12] ■ ■ ■ ■ [ K] ■ ■ ■ ■ ---------- 表向き: 2 / 52 手数: 2 最後に動かしたカード: ♠8 ========== ``` - `[N]`: 位置番号(1〜12は時計の位置、Kは中央) - `♠A(表)`: 表向きのカード - `■`: 伏せカード - `表向き: N / 52`: 表向きになったカードの枚数 - `手数: N`: これまでに処理したカードの枚数 - `最後に動かしたカード`: 直前のステップで表向きになったカード ## 遊び方のコツ - このゲームはシャッフル後の運だけで決まる純粋な確率ゲームです - クリア確率は約1/13(約7.7%)と言われています - `autoplay` コマンドを使うと一気にゲームを進められます - `step` コマンドで1手ずつ確認しながら進めると、ゲームの仕組みを理解しやすいです - 結果に関わらず `log` でカードの動きを振り返ることができます - 何度もリセットして挑戦してみましょう # クレイジーエイト(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。捨て札のトップカードとスートまたはランクが一致するカードを出していき、先に手札を全て出し切ったプレイヤーがラウンドの勝者です。ポイント上限(デフォルト200点)に達したプレイヤーが出たらマッチ終了です。 ## 起動方法 ```sh go run ./cmd/trumpcards crazyeights go run ./cmd/trumpcards --lang en crazyeights # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを使用、4人プレイ - 各プレイヤーに5枚ずつ配り、1枚を表向きにして捨て札の山を開始、残りは山札 - 捨て札の山のトップカードとスートまたはランクが一致するカードを出す - **8はワイルド**: 出すと次のスートを選べる - 出せるカードがなければ山札から1枚ドロー。出せればそのまま続行、出せなければパス - 山札が空になったら捨て札(トップを除く)をリサイクルして山札にする ### 得点 - ラウンド終了時、他プレイヤーの手札がスコアとして加算される - 8=50点、J/Q/K=10点、A=1点、その他=額面 ### ゲーム終了 - ポイント上限(デフォルト200点)に達したプレイヤーが出たらマッチ終了 ### CPU難易度 - **Easy** (0): ランダムにカードを出す - **Normal** (1): 基本的な戦略(デフォルト) - **Hard** (2): 戦略的なプレイ(8の温存等) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各5枚] B --> C[捨て札の山に1枚表向き] C --> D{プレイヤーの手番?} D -->|はい| E{出せるカードあり?} D -->|いいえ| F[CPUが自動でプレイ] E -->|はい| G[カードを出す] E -->|いいえ| H[山札から1枚ドロー] H --> I{ドローしたカードが出せる?} I -->|はい| G I -->|いいえ| J[パス] G --> K{8を出した?} K -->|はい| L[スート選択] K -->|いいえ| M{手札が0枚?} L --> M J --> D F --> D M -->|はい| N[ラウンド終了 - スコア計算] M -->|いいえ| D N --> O{ポイント上限到達?} O -->|いいえ| B O -->|はい| P[マッチ終了 - 結果表示] P -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `play ` | `p ` | カードをプレイ(インデックス指定) | | `draw` | `d` | 山札から1枚ドロー | | `suit <1-4>` | `s <1-4>` | スート選択(8を出した後): 1=♠, 2=♣, 3=♥, 4=♦ | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `setlimit ` | `sl ` | ポイント上限設定 | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `p 2` → インデックス2のカードを出す - `d` → 山札から1枚ドロー - `s 3` → スートを♥に設定(8を出した後) - `sd 2` → CPU難易度をHardに設定 ## 画面の見方 ``` ========== Crazy Eights (クレイジーエイト) ========== ラウンド 1 ---------- [You]: 0点 [0]SPADE 3 [1]CLOVER 8 [2]HEART Q [3]DIAMOND 5 [4]SPADE J CPU 1: 0点 (5枚) CPU 2: 0点 (4枚) CPU 3: 0点 (6枚) ---------- 捨て札トップ: HEART 7 山札残り: 27枚 手番: あなた p ・・・カードを出す d・・・ドロー ========== ``` - `ラウンド N`: 現在のラウンド番号 - `[You]: N点`: プレイヤーの累計得点 - `[0]SPADE 3`...: インデックス付きの手札カード - `CPU N: M点 (X枚)`: CPUの累計得点と手札枚数 - `捨て札トップ`: 捨て札の山の一番上のカード - `山札残り`: 山札の残り枚数 ## 遊び方のコツ - 8(ワイルド)は切り札として温存し、出せるカードがない時に使いましょう - 相手の手札枚数が少なくなったら、出しにくいスートに切り替えて妨害しましょう - 同じスートのカードが多い場合は、そのスートを続けて出すと有利です - 高得点カード(8=50点、J/Q/K=10点)は早めに処理してリスクを減らしましょう # クリベッジ(CUI版)遊び方 ## ゲーム概要 2人対戦のカードゲームです。52枚のカードを使い、ペギング(カードを交互に出して得点)とハンドスコアリング(手札の組み合わせで得点)を繰り返し、先に121点に到達したプレイヤーが勝利します。 ## 起動方法 ```sh go run ./cmd/trumpcards cribbage go run ./cmd/trumpcards --lang en cribbage # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 2人対戦(1人 vs CPU) - 各ラウンドでディーラーが交互に入れ替わる - 先に121点(設定変更可能)に到達したプレイヤーが勝利 ### ラウンドの流れ 1. **ディスカード**: 各プレイヤーに6枚配り、2枚ずつクリブ(ディーラーのボーナス手札)に捨てる 2. **カット**: 山札からスターターカードを1枚公開(Jならディーラーに2点 = His Heels) 3. **ペギング**: 非ディーラーから交互にカードを出し、合計31を目指す 4. **ショー**: 手札4枚+スターターカードの組み合わせでスコア計算 ### カードの値(ペギング用) | カード | 値 | |--------|-----| | A | 1 | | 2〜10 | 数字通り | | J, Q, K | 10 | ### ペギングの得点 | 条件 | 得点 | |------|------| | 合計が15になる | 2点 | | ペア(同ランク2枚) | 2点 | | ペアロイヤル(同ランク3枚) | 6点 | | ダブルペアロイヤル(同ランク4枚) | 12点 | | ラン(3枚以上の連番) | 枚数分 | | 合計が31になる | 2点 | | Go(相手が出せない) | 1点 | | ラストカード | 1点 | ### ハンドスコアリング(ショーフェーズ) 手札4枚+スターターカードの5枚で以下を計算: | 組み合わせ | 得点 | |-----------|------| | 15の組み合わせ | 各2点 | | ペア | 各2点 | | ラン(3枚以上の連番) | 枚数分 | | フラッシュ(手札4枚同スート) | 4点 | | フラッシュ(5枚同スート) | 5点 | | ノブス(スターターと同スートのJ) | 1点 | ### CPU難易度 | 難易度 | 説明 | |--------|------| | Easy (0) | ランダムにプレイ | | Normal (1) | 基本的な戦略でプレイ | | Hard (2) | 最適なディスカードとペギング戦略 | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[6枚配る] B --> C[ディスカード: 2枚をクリブに捨てる] C --> D[カット: スターターカード公開] D --> E[ペギング: 交互にカードを出す] E --> F{合計31 or 全員Go?} F -->|31 or Go| G{手札残りあり?} G -->|はい| E G -->|いいえ| H[ショー: スコア計算] H --> I[非ディーラー手札 → ディーラー手札 → クリブ] I --> J{121点到達?} J -->|いいえ| K[次のラウンド] K --> B J -->|はい| L[ゲーム終了] L -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `discard ` | `d` | 指定インデックスの2枚をクリブに捨てる | | `peg ` | `p` | 指定インデックスのカードをペギングで出す | | `go` | | Goを宣言(カードを出せない場合) | | `shownext` | `sn` | ショーフェーズの次のステップへ進む | | `nextround` | `nr` | 次のラウンドを開始 | | `setdifficulty <0-2>` | `sd` | CPU難易度を設定 | | `setlimit ` | `sl` | 目標点数を設定 | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `d 4 5` → 手札の5番目と6番目のカードをクリブに捨てる - `p 0` → 手札の1番目のカードをペギングで出す - `go` → カードを出せない場合にGoを宣言 - `sn` → ショーフェーズの次のステップへ - `nr` → 次のラウンドを開始 - `sd 2` → CPU難易度をHardに設定 - `sl 61` → 目標点数を61点に設定 ## 画面の見方 ``` ========== Cribbage (クリベッジ) ========== ラウンド: 1 ディーラー: CPU スターター: [♠J] ---------- あなた: 12点 CPU: 8点 手札: [♥3] [♦7] [♠9] [♣K] クリブ: 4枚 ペギング合計: 15 ---------- d・・・捨てる p・・・出す go・・・Go sn・・・次へ ========== ``` - `ラウンド: N`: 現在のラウンド番号 - `ディーラー`: 現在のラウンドのディーラー - `スターター`: カットされたスターターカード - `あなた: N点 CPU: M点`: 各プレイヤーの累計スコア - `手札`: 自分の手札 - `クリブ: N枚`: クリブのカード枚数 - `ペギング合計: N`: 現在のペギングカウント ## 遊び方のコツ - ディスカードでは、クリブがディーラーのものであることを意識しましょう(自分がディーラーならクリブに良い組み合わせを、非ディーラーなら避けましょう) - 15の組み合わせを作りやすいカード(5や10系カード)を手札に残すと高得点になりやすいです - ペギングでは相手に15や31を作らせないよう注意しましょう - ランを狙える連番のカードは手札に残しておくと有利です - ショーフェーズでのスコアリングをよく理解し、ディスカードの判断に活かしましょう # 大富豪(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。手札を場に出していき、最初に手札をなくしたプレイヤーが「大富豪」、最後まで残ったプレイヤーが「大貧民」になります。 ## 起動方法 ```sh go run ./cmd/cli daifugo ``` ## ルール ### カードの強さ(通常時) ``` 弱い ← 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A < 2 → 強い ``` ジョーカーは常に最強です。 ### デッキ 52枚の標準トランプ + ジョーカー2枚(合計54枚) ### 順位 | 順位 | 称号 | |------|------| | 1位 | 大富豪 | | 2位 | 富豪 | | 3位 | 平民 | | 4位 | 大貧民 | ### ローカルルール CUI版では以下のルールが**常時有効**です: | ルール | 説明 | |--------|------| | 8切り | 8を出すと場が流れ、出したプレイヤーが続けて出せる | | スート縛り | 同じスートが連続で出ると、以降そのスートのカードしか出せない(モード設定可能: なし / 片縛り / 両縛り) | | 11バック | Jを出すとカードの強さが一時的に逆転する | | 階段 | 同じスートの連続3枚以上を一度に出せる | | カード交換 | 2ラウンド目以降、大富豪⇔大貧民で2枚、富豪⇔平民で1枚を交換 | | 革命 | 4枚同時出しでカードの強さが永続的に逆転(再度4枚出しで元に戻る) | 以下のルールはデフォルトで**無効**です(`sr` コマンドで切り替え可能): | ルール | 説明 | |--------|------| | 5飛び | 5を出すと次のプレイヤーの手番がスキップされる(スキップ人数は設定可能、デフォルト1) | | 7渡し | 7を出すと次のプレイヤーに任意のカード1枚を渡さなければならない | | 10捨て | 10を出すと手札から任意のカード1枚を捨てなければならない | | スペ3返し | 場がジョーカー1枚のとき、スペードの3で返せる | | 都落ち | 前ラウンドの大富豪が2位以下になった場合、最下位の順位と入れ替わる | | 9リバース | 9を出すとターンの進行方向が逆転する(再度9を出すと元に戻る) | | クーデター | 9を3枚同時に出すと革命が発生する | | 数縛り | 同じ数字が連続で出ると、以降その数字のカードしか出せない(スート縛りから独立した設定) | | 砂嵐 | ジョーカーでない3を3枚同時に出すと場が流れる(8切りと同様の効果) | | エンペラー | 場が空の状態で、4枚の連番カード(すべて異なるスート)を出すと革命が発生し場が流れる | | 階段革命 | 4枚以上の階段を出すと革命が発生する | | 階段縛り | 階段の上に階段を出すと階段縛りが発動する。場が流れてもリセットされず、ゲームリセット時のみ解除される。縛り中に場が空の場合、3枚以上の階段またはエンペラーのみ出せる | | 反則上がり | 8切り・ジョーカー・革命で上がると反則となり、最下位に降格される | | 12ボンバー | Qを出すと数字を選び、全プレイヤーからその数字のカードを除去する | | ブラインドカード交換 | カード交換時に、上位のプレイヤーが最も弱いカードではなくランダムなカードを渡す | ### CPU難易度 CUI版のCPU難易度は**ふつう(Normal)**固定です。 | 難易度 | 説明 | |--------|------| | よわい(Easy) | 単純に出せる中で最も弱いカードを出す。8やジョーカーの温存、革命防止をしない | | ふつう(Normal) | バランスの取れたAI。8やジョーカーを温存し、革命を防止する(デフォルト) | | つよい(Hard) | 相手の手札枚数を考慮し、相手が残り少ない場合は強いカードから出す。戦略的にパスする場合もある | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B{2ラウンド目以降?} B -->|はい| C[カード交換] B -->|いいえ| D[カード配布] C --> D D --> E[CPUが自動で手番を進行] E --> F{プレイヤーの手番?} F -->|はい| G{カードを出す?} F -->|いいえ| H[CPUが自動でプレイ] H --> F G -->|play N N...| I[カードを場に出す] G -->|play| J[パス] I --> K{全員パス?} J --> K K -->|はい| L[場を流す → 最後に出した人から] K -->|いいえ| E L --> E E --> M{全員終了?} M -->|はい| N[順位発表] N -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいラウンドを開始 | | `play N N...` | `p N N...` | 指定インデックスのカードを出す(例: `p 0 2`) | | `play` | `p` | パス(カードを出さない) | | `sort N` | - | 手札のソート方法を変更(0=強さ順、1=スート順、2=数字順) | | `setrule <0\|1>` | `sr <0\|1>` | ローカルルールの有効/無効を切り替え(リセットして反映) | | `suitlockmode N` | - | スート縛りモードを設定(0=なし、1=片縛り、2=両縛り) | | `5skipcount N` | - | 5飛びスキップ人数を設定(1〜5) | | `setdifficulty N` | `sd N` | CPU難易度を設定(0=Normal、1=Easy、2=Hard) | | `setjoker N` | `sj N` | ジョーカー枚数を設定(0〜2) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### ルール設定コマンド(`sr`) `setrule`(短縮形: `sr`)で各ローカルルールの有効/無効を切り替えます。設定変更後は自動でリセットされます。 使用可能なルールキー: | キー | 対応ルール | |------|-----------| | `8cut` | 8切り | | `11back` | 11バック | | `seq` | 階段 | | `exchange` | カード交換 | | `5skip` | 5飛び | | `7pass` | 7渡し | | `10discard` | 10捨て | | `spade3` | スペ3返し | | `capital` | 都落ち | | `9reverse` | 9リバース | | `coupdetat` | クーデター | | `numberlock` | 数縛り | | `sandstorm` | 砂嵐 | | `emperor` | エンペラー | | `seqrev` | 階段革命 | | `seqlock` | 階段縛り | | `illegal` | 反則上がり | | `12bomber` | 12ボンバー | | `blindexchange` | ブラインドカード交換 | 例: ``` sr 5skip 1 # 5飛びを有効にする sr 12bomber 1 # 12ボンバーを有効にする sr numberlock 1 # 数縛りを有効にする suitlockmode 2 # スート縛りを両縛りに設定 5skipcount 3 # 5飛びで3人スキップに設定 ``` ## カードの出し方 ### 基本 - 場にカードがない場合: 好きなカードを出せます - 場にカードがある場合: 同じ枚数かつ場より強いカードを出す必要があります ### 複数枚出し - 同じ数字のカードは複数枚同時に出せます(例: `p 3 7` で同じ数字のカード2枚) - ジョーカーはワイルドカードとして任意のカードの代わりになります ### 階段 - 同じスートの連続する3枚以上を一度に出せます(例: SPADE 5, SPADE 6, SPADE 7) ## 画面の見方 ``` ========== Daifugo (大富豪) ========== [You]: 13枚 [0]SPADE 3 [1]CLOVER 5 [2]HEART 8 ... CPU 1: 13枚 CPU 2: 上がり (ランク: 大富豪) CPU 3: 13枚 ---------- 【革命中】2が最弱、3が最強 【スート縛り】SPADE 場: SPADE 10, SPADE J (出したプレイヤー: CPU 1) ---------- ``` - `[You]: N枚`: プレイヤーの手札枚数とカード一覧 - `CPU N: M枚`: CPUの手札枚数(カード内容は非表示) - `上がり (ランク: ...)`: そのプレイヤーは手札がなくなり、順位が確定 - `[0]`, `[1]`...: カードのインデックス(`play` で指定) - `場:`: 現在場に出ているカード - `【革命中】` / `【スート縛り】` / `【11バック】` / `【階段】` / `【9リバース】` / `【数縛り】`: 有効なルールの表示 ## カード交換(2ラウンド目以降) ラウンド開始時に前ラウンドの結果に基づいてカード交換が行われます: - 大貧民 → 大富豪: 最も強いカード2枚を渡す - 大富豪 → 大貧民: 最も弱いカード2枚を渡す - 平民 → 富豪: 最も強いカード1枚を渡す - 富豪 → 平民: 最も弱いカード1枚を渡す # デューシーズワイルド(CUI版)遊び方 ## ゲーム概要 1人用カジノカードゲームです。52枚のトランプを使い、5枚のカードでポーカーの役を作ります。すべての2がワイルドカードとして扱われ、他の任意のカードの代わりになります。Deuces Wild専用のペイテーブルに基づいて配当が支払われます。 ## 起動方法 ```sh go run ./cmd/trumpcards deuceswild go run ./cmd/trumpcards --lang en deuceswild # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - すべての2(デュース)がワイルドカード - 1〜5コインをベットしてゲーム開始 - 5枚のカードが配られる - 残したいカード(ホールド)を選び、残りを交換 - 最終的な5枚の手札で役を判定し、配当を支払い ### 配当表(Deuces Wild) | 役 | 1コイン | 2コイン | 3コイン | 4コイン | 5コイン | |----|---------|---------|---------|---------|---------| | ナチュラルロイヤルフラッシュ | 250 | 500 | 750 | 1000 | 4000 | | フォーデュース | 200 | 400 | 600 | 800 | 1000 | | ワイルドロイヤルフラッシュ | 25 | 50 | 75 | 100 | 125 | | ファイブカード | 15 | 30 | 45 | 60 | 75 | | ストレートフラッシュ | 9 | 18 | 27 | 36 | 45 | | フォーカード | 5 | 10 | 15 | 20 | 25 | | フルハウス | 3 | 6 | 9 | 12 | 15 | | フラッシュ | 2 | 4 | 6 | 8 | 10 | | ストレート | 2 | 4 | 6 | 8 | 10 | | スリーカード | 1 | 2 | 3 | 4 | 5 | ### ワイルドカード - すべての2(デュース)は任意のカードとして扱える - ナチュラルロイヤルフラッシュ: ワイルドなしのロイヤルフラッシュ(最高配当) - ワイルドロイヤルフラッシュ: ワイルドを含むロイヤルフラッシュ - フォーデュース: 4枚の2が揃う特別役 - ファイブカード: ワイルドを使って同じランク5枚 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ベットフェーズ] B --> C[b コイン数 でベット] C --> D[5枚のカードが配られる] D --> E[ドローフェーズ] E --> F[h インデックス でホールドするカードを選択] F --> G[残りのカードを交換] G --> H[役判定・配当] H -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `bet ` | `b ` | ベットする(1〜5コイン) | | `hold ` | `h ` | カードをホールドして交換(インデックス0〜4) | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `r` → ゲームリセット - `b 5` → 5コインベット - `h 0 2 4` → 0番目、2番目、4番目のカードをホールドし、残りを交換 - `h` → 全カード交換(ホールドなし) - `l` → 棋譜を表示 ## 画面の見方 ``` ========== デューシーズワイルド ========== チップ: 100 ---------- ベットフェーズ b・・・ベット l・・・棋譜 ========== > b 3 ========== デューシーズワイルド ========== チップ: 97 ---------- [0]♠A [1]♥2 [2]♦K [3]♣3 [4]♠J ---------- ドローフェーズ h・・・ホールド l・・・棋譜 ========== > h 0 1 2 ========== デューシーズワイルド ========== チップ: 106 ---------- [0]♠A [1]♥2 [2]♦K [3]♥A [4]♦9 ---------- スリーカード 配当: 9 ---------- r・・・リセット l・・・棋譜 ========== ``` - `チップ: N`: 所持チップ数 - `[N]♠A`: カードのインデックスとカード表示 - `配当: N`: 獲得配当 ## 遊び方のコツ - 5コインベットでナチュラルロイヤルフラッシュの配当が大幅に増える(250→4000)ため、可能なら最大ベットがお得 - 2(デュース)は常にホールドする - デュースを持っている場合、より高い役を狙いやすい - デュースなしの場合はJacks or Betterより最低配当条件が厳しい(スリーカード以上が必要) # ダウト(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。カードを場に出す際に宣言する値が本当かどうかを見極めながら、手札を最初になくしたプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli doubt ``` ## ルール ### 基本ルール - 52枚のトランプを4人に配布(1人13枚) - 順番にカードを1枚以上場に出し、出した枚数と宣言する値を伝える - 宣言する値は本当でも嘘でも構わない(ブラフ可) - 他のプレイヤーは「ダウト(嘘だ)」または「スルー(信じる)」を選択する - **ダウトの結果**: - 宣言が嘘だった場合 → カードを出したプレイヤーが場のカードをすべて引き取る - 宣言が本当だった場合 → ダウトしたプレイヤーが場のカードをすべて引き取る - 最初に手札をなくしたプレイヤーが勝者 ### ダウト判定ウィンドウ - カードが場に出された後、設定した秒数(デフォルト **10秒**)のダウト判定ウィンドウがある - CPU がカードを出した場合: 制限時間内に `d`(ダウト)または Enter(スルー)を入力する - あなたがカードを出した場合: CPU がダウトするかどうかを即時自動判定する - ダウト判定ウィンドウの長さは、CUI版では `sw ` コマンドで変更できます(デフォルト10秒)。Web版では設定パネルから変更できます ### CPU のメモリ・推理 AI - CPU は公開されたカード(ダウト解決時)を記憶する - 記憶したカードと自分の手札を合わせて「**物理的に不可能な宣言**」を検出すると 100% ダウトを宣言する (例: 既に4枚すべての「7」が確認済みなのにさらに「7」が宣言された場合) - 通常時は 30% の確率でランダムにダウトを宣言する - 記憶の精度(保持確率)は `sm ` コマンドで変更可能: - `DoubtMemoryLevelEasy` (0): 約 30% 保持 - `DoubtMemoryLevelNormal` (1): 約 70% 保持(デフォルト) - `DoubtMemoryLevelHard` (2): 100% 保持 ### 記憶の減衰 - CPU の記憶はターンが進むにつれて確率的に忘れていく(人間らしい忘却) - 忘却確率 = 減衰率 × 経過ターン数(古い記憶ほど忘れやすい) - 減衰率は記憶力レベルに連動: - Easy: 0.15/ターン(忘れやすい) - Normal: 0.05/ターン - Hard: 0.0(忘れない) ### ペナルティ上限(PenaltyDrawLimit) - ダウト敗者が引き取るカードの上限枚数を設定できる - 0(デフォルト)= 無制限(敗者が場のカードをすべて引き取る) - 上限を超えた分のカードはゲームから除外される(誰の手札にも入らない) - CUI版では `sp ` コマンドで変更可能 - Web版では設定パネルから変更できる ### 動的ブラフ確率 - CPU のブラフ確率は状況に応じて変動する(基本 40%) - 手札残り1枚: 10%(リスク回避) - テーブルに20枚以上: 基本から −15%(引き取りリスクが大きい) - テーブルに10枚以上: 基本から −10% ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各13枚] B --> C[CPUが自動で手番を進行] C --> D{プレイヤーの手番?} D -->|はい| E[コマンド入力] D -->|いいえ| F[CPUが自動でカードを出す] F --> G[ダウト判定フェーズ] E -->|p 値 idx...| H[カードを宣言して出す] H --> G G --> I{CPUがカードを出した?} I -->|はい| J[10秒間ダウト待機] I -->|いいえ| K[CPUが即時ダウト判定] J -->|d または doubt| L[ダウト!] J -->|Enter またはタイムアウト| M[スルー] K --> N{ダウトあり?} L --> N M --> N N -->|はい| O{宣言は嘘?} N -->|いいえ| C O -->|はい| P[カード出しプレイヤーが引き取り] O -->|いいえ| Q[ダウトしたプレイヤーが引き取り] P --> R{手札が空?} Q --> R R -->|はい| S[勝者確定] R -->|いいえ| C S --> T[ゲーム終了表示] T -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `play 値 idx...` | `p 値 idx...` | 手札のカードを出す(値=宣言値, idx=手札インデックス) | | `d` または `doubt` | - | ダウト!(ダウト判定ウィンドウ中のみ有効) | | `s` | - | スキップ(ダウトしない) | | `setwindow ` | `sw ` | ダウト判定ウィンドウの秒数を設定(1〜60) | | `setmemory ` | `sm ` | CPU記憶力レベルを設定(0=Easy, 1=Normal, 2=Hard) | | `setpenalty ` | `sp ` | ペナルティドロー上限を設定(0=無制限, >0=上限) | | `setmetaai N` | `smai N` | Meta-AI の切替(0=OFF, 1=ON) | | `resetprofile` | `rp` | Meta-AI のプロファイル(学習データ)をリセット | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### `play` コマンドの詳細 `p 値 idx...` の各引数: | 引数 | 説明 | |------|------| | `値` | 宣言する値(1〜13。1=A、11=J、12=Q、13=K) | | `idx...` | 場に出す手札のインデックス(複数指定可) | 例: - `p 5 0` → インデックス0のカードを「5」と宣言して1枚出す - `p 3 0 2` → インデックス0と2のカードを「3」と宣言して2枚出す - `p 1 0 1 2` → インデックス0・1・2のカードを「A」と宣言して3枚出す ## 画面の見方 ``` ========== Doubt (ダウト) ========== [You]: 11枚 [0]SPADE 2 [1]CLOVER 5 [2]HEART 9 [3]DIAMOND 11 ... CPU 1: 13枚 CPU 2: 12枚 CPU 3: 上がり ---------- テーブル: 3枚 [最後のプレイ] CPU 2が「7」を2枚出しました [ダウト] あなたがCPU 2をダウト → 嘘つき! CPU 2が5枚引き取りました (2枚がゲームから除外されました) 公開カード: HEART 3, SPADE 11 手番: あなた p <値> ・・・カードを出す ========== ``` - `[You]: N枚`: プレイヤーの手札枚数 - `[0]SPADE 2`, `[1]CLOVER 5`...: インデックス付きの手札カード(`play` で指定) - `CPU N: M枚`: CPU の手札枚数 - `上がり`: 手札がなくなったプレイヤー - `テーブル: N枚`: 現在場に積まれているカードの枚数 - `[最後のプレイ]`: 直前に出されたカードの情報(誰が、何を宣言して何枚出したか) - `[ダウト]`: ダウト判定の結果(嘘/正直、引き取り枚数、除外枚数、公開カード) - `[あなたの行動]`: 自分が出したカードの宣言情報 - `[CPUの行動]`: CPU が出したカードの宣言情報 ## ダウトウィンドウの見方 CPU がカードを出した後、以下のメッセージが表示されます: ``` ダウト!と言いますか? (d / doubt → ダウト、Enter でスキップ) [N秒] ``` - `d` または `doubt` を入力してEnter → ダウトを宣言 - Enter のみ(または何も入力せず制限時間経過) → スルー(ダウトしない) ## 遊び方のコツ - 手持ちのカードを見て、宣言と一致するカードが多い値を宣言すると安全です - 相手がブラフを続けているようであれば積極的にダウトしましょう - 逆に正直に出し続けることで相手を油断させる戦略も有効です - 場のカードが多いときにダウトして成功すると、相手に大量のカードを引き取らせられます - 場のカードが少ないときにダウトを宣言してもリスクが低いため、疑わしいときは早めに宣言しましょう # ユーカー(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶ2チーム制(0+2 vs 1+3)のトリックテイキング型カードゲームです。24枚のカード(各スート9〜A)を使用し、ライトバウアー(切り札のJ)とレフトバウアー(同色スートのJ)が最強の切り札です。先に10点に到達したチームが勝者です。 ## 起動方法 ```sh go run ./cmd/trumpcards euchre go run ./cmd/trumpcards --lang en euchre # 英語モード ``` ## ルール ### 基本ルール - 24枚のカード(各スート9, 10, J, Q, K, A)を4人に配布(1人5枚) - 4人を2チームに分ける(プレイヤー0+2 vs プレイヤー1+3) - ディーラーが1枚めくり(ターンアップカード)、そのスートが切り札候補 - **ライトバウアー**(切り札のJ)が最強カード - **レフトバウアー**(切り札と同色スートのJ)が2番目に強い切り札 - リードスートに従う(フォロースート) - トリックの勝者: 切り札が出ていれば最高の切り札、なければリードスートの最高値 ### ビッド(切り札決定) - **ピックアップフェーズ**: ターンアップカードのスートを切り札にするか各プレイヤーが判断 - オーダーアップ: ディーラーがターンアップカードを拾い、1枚捨てる - パス: 次のプレイヤーへ - **コールトランプフェーズ**: 全員パスした場合、ターンアップ以外のスートを切り札に宣言 - **ゴーイングアローン**: オーダーアップ/コール時にアローンを選択可(パートナーが休み) ### 得点 - **メイカー(切り札を決めたチーム)3〜4トリック**: +1点 - **マーチ(5トリック全取り)**: +2点 - **ユーカード(ディフェンダーが勝利)**: ディフェンダー +2点 - **ゴーイングアローン マーチ**: +4点 - **ゴーイングアローン 3〜4トリック**: +1点 ### ゲーム終了 - **勝利条件**: 先に10点に到達 ### CPU難易度 - **Easy** (0): ランダムにカードを出す - **Normal** (1): 基本的なトリック戦略(デフォルト) - **Hard** (2): 戦略的なプレイ(切り札管理等) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各5枚] B --> C[ターンアップカード公開] C --> D[ピックアップフェーズ] D --> E{オーダーアップ?} E -->|はい| F[ディーラーが拾い1枚捨てる] E -->|全員パス| G[コールトランプフェーズ] G --> H[切り札スートを宣言] F --> I[プレイフェーズ開始] H --> I I --> J{プレイヤーの手番?} J -->|はい| K[カードを選んで出す] J -->|いいえ| L[CPUが自動でカードを出す] K --> M[トリック完了] L --> M M --> N{5トリック終了?} N -->|いいえ| J N -->|はい| O[ラウンド終了 - 得点計算] O --> P{10点到達?} P -->|いいえ| B P -->|はい| Q[ゲーム終了 - 結果表示] Q -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `orderup` | `o` | ターンアップカードをオーダーアップ | | `orderup alone` | `oa` | オーダーアップしてゴーイングアローン | | `pass` | `pa` | パス(オーダーアップ/コールを見送る) | | `call ` | `c ` | 切り札スートを宣言(s=スペード, c=クラブ, h=ハート, d=ダイヤ) | | `call alone` | `ca ` | 切り札を宣言してゴーイングアローン | | `discard ` | `d ` | カードを捨てる(ディーラーのみ、インデックス指定) | | `play ` | `p ` | カードをプレイ(インデックス指定) | | `next` | `n` | 次のトリックへ | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `setlimit ` | `sl ` | ポイント上限設定 | | `hint` | `h` | 推奨カードのヒントを表示 | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `o` → ターンアップカードをオーダーアップ - `oa` → オーダーアップしてゴーイングアローン - `pa` → パス - `c h` → ハートを切り札に宣言 - `ca s` → スペードを切り札にしてゴーイングアローン - `d 2` → インデックス2のカードを捨てる - `p 0` → インデックス0のカードを出す ## 画面の見方 ``` ========== Euchre (ユーカー) ========== ラウンド 3 / トリック 2 切り札: HEART ディーラー: CPU 2 チーム1(あなた+CPU 2): 6点 チーム2(CPU 1+CPU 3): 4点 ---------- [You]: 5枚 [0]HEART A [1]HEART J [2]SPADE Q [3]DIAMOND K [4]CLOVER 9 CPU 1: 5枚 CPU 2: 5枚 (ディーラー) CPU 3: 5枚 ---------- 現在のトリック: CPU 1: DIAMOND 10 手番: あなた p ・・・カードを出す ========== ``` - `ラウンド N / トリック M`: 現在のラウンドとトリック番号 - `切り札`: 現在の切り札スート - `ディーラー`: 現在のディーラー - `チームN: X点`: 各チームの累計得点 - `[0]HEART A`...: インデックス付きの手札カード - `現在のトリック`: トリックに出されたカード ## 遊び方のコツ - ライトバウアー(切り札のJ)やレフトバウアー(同色のJ)を持っている場合はオーダーアップ/コールを積極的に検討しましょう - ゴーイングアローンは手札が非常に強い場合にのみ狙いましょう(マーチで+4点) - パートナーとの連携を意識し、パートナーがリードしたスートをフォローしましょう - 切り札が少ない場合は、特定のスートをボイド(手札からなくす)にして切り札で切る戦略が有効です - ディフェンダーの場合、メイカーをユーカードにすると+2点の大チャンスです - 迷った時は `h` コマンドでヒントを表示できます # フリーセル(CUI版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、8列のタブロー、4つのフリーセル、4つの組札で構成されます。すべてのカードが表向きに配られ、組札にA→Kまでスート別に積み上げればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards freecell ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 8列のタブローに配る(最初の4列に7枚ずつ、残りの4列に6枚ずつ)、すべて表向き - 4つのフリーセル: 一時的にカード1枚を置ける場所 - 4つの組札(ファンデーション)はスート別にA→Kの順に積み上げる - タブローでは降順かつ交互の色で重ねる(例: 黒のKの上に赤のQ) - 空いた列にはKのみ置くことができる ### スーパームーブ 一度に移動できるカード数は以下の計算式で決まります: ``` 最大移動枚数 = (1 + 空きフリーセル数) × 2^(空きタブロー列数) ``` ### ゲームクリア - 4つの組札すべてにA→Kまで13枚ずつ積み上げるとゲームクリア ### ゲームオーバー - これ以上移動可能な手がない場合はゲームオーバー ### 手詰まり検出 - 各操作の後、ソルバー(DFS + メモ化)がボード全体を解析し、解法が存在しないと判定された場合「手詰まりです」と表示します - 手詰まり状態では「元に戻す」またはギブアップを選択できます - ソルバーの探索上限(100,000状態)を超えた場合は手詰まりとは判定しません ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カードを配る] B --> C{移動可能な手がある?} C -->|はい| D{プレイヤーのアクション} D -->|move| E[カードを移動] D -->|hint| F[ヒントを表示] D -->|autocomplete| G[自動で組札に積み上げ] D -->|undo| H[直前の操作を元に戻す] E --> I{組札が完成?} F --> C G --> I H --> C I -->|いいえ| C I -->|はい| J[ゲームクリア] C -->|いいえ| K[ゲームオーバー] J -->|reset| A K -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `move` | `m` | カードを移動する(サブ引数で移動元・移動先を指定) | | `undo` | `u` | 直前の操作を元に戻す | | `giveup` | `g` | ギブアップしてゲームを終了 | | `hint` | `h` | 次の一手のヒントを表示 | | `autocomplete` | `ac` | 自動的に組札へ積み上げ可能なカードを移動 | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### 移動コマンドの構文 移動元・移動先にはゾーン名とインデックスを指定します: - `tableau <列番号>` — タブローの列(0〜7) - `freecell <セル番号>` — フリーセル(0〜3) - `foundation <列番号>` — 組札(0〜3) ### コマンド例 - `m` → 移動コマンド(移動元・移動先の入力を求められる) - `u` → 直前の操作を元に戻す - `h` → ヒントを表示 - `ac` → オートコンプリート ## 画面の見方 ``` ========== FreeCell (フリーセル) ========== フリーセル: [♠K] [ ] [ ] [ ] ---------- 組札: [♠] A [♥] A 2 [♦] -- [♣] -- ---------- タブロー: [0] ♥Q ♣J ♦10 ♠9 ♥8 ♣7 ♦6 [1] ♠J ♥10 ♣9 ♦8 ♠7 ♥6 [2] ♦Q ♣K ♥J ♠10 ♦9 ♣8 ♥7 [3] ♣Q ♦J ♠Q ♥K ♦K ♣10 ♠8 [4] ♥9 ♣6 ♦5 ♠4 ♥3 ♣2 [5] ♠6 ♥5 ♦4 ♣3 ♠2 ♥4 [6] ♦3 ♣5 ♠3 ♥2 ♦2 ♣4 [7] ♠5 ♦7 ♣A ♠A ♦A ♥A ---------- 手数: 15 m・・・カードを移動 h・・・ヒント ac・・・オートコンプリート ========== ``` - `フリーセル: [♠K] [ ]`: フリーセルの状態(カードまたは空) - `組札`: 各スートの組札の状態(`--`は空) - `[0] ♥Q ♣J ...`: タブロー列(すべて表向き) - `手数: N`: 現在の手数 ## 遊び方のコツ - フリーセルは一時的な退避場所です。むやみに埋めないようにしましょう - 空いたタブロー列はスーパームーブの計算に影響するため、戦略的に活用しましょう - Aが出たらすぐに組札へ移動しましょう - 空いた列にはKのみ置くことができます - ヒントコマンドで詰まった時のアドバイスを得られます - オートコンプリートは組札に安全に移動できるカードがある場合に便利です # ジンラミー(CUI版)遊び方 ## ゲーム概要 2人(プレイヤー1人 + CPU 1人)で遊ぶカードゲームです。手札のカードでメルド(セットやラン)を作り、デッドウッド(メルドに含まれないカード)の合計点を減らします。ポイント上限(デフォルト100点)に先に達したプレイヤーが敗北します。 ## 起動方法 ```sh go run ./cmd/trumpcards ginrummy go run ./cmd/trumpcards --lang en ginrummy # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを使用、2人プレイ - 各プレイヤーに10枚ずつ配り、1枚を表向きにして捨て札の山を開始、残りは山札(ストック) - 手番ではまず山札または捨て札の山からカードを1枚ドローする - その後、手札からカードを1枚捨てるか、ノックする ### メルドとデッドウッド - **メルド**: セット(同ランク3〜4枚)またはラン(同スート3枚以上の連番) - **デッドウッド**: メルドに含まれない手札の合計点(J/Q/K=10点、A=1点、その他=額面) ### ノック・ジン・アンダーカット - **ノック**: デッドウッド合計が10以下の時にノック可能。相手はノッカーのメルドにレイオフ(カードを付け足す)できる - **ジン**: デッドウッド0でノック。25点ボーナス。相手はレイオフ不可 - **アンダーカット**: 相手のデッドウッドがノッカー以下の場合、相手に25点ボーナス ### ゲーム終了 - ポイント上限(デフォルト100点)に達したプレイヤーが出たらマッチ終了 ### CPU難易度 - **Easy** (0): ランダムにカードを出す - **Normal** (1): 基本的なメルド戦略(デフォルト) - **Hard** (2): 戦略的なプレイ(相手の捨て札を分析等) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各10枚] B --> C[捨て札の山に1枚表向き] C --> D{プレイヤーの手番?} D -->|はい| E[山札 or 捨て札からドロー] D -->|いいえ| F[CPUが自動でドロー] E --> G{ノック可能?} G -->|ノック| H[ノック宣言] G -->|捨てる| I[カードを1枚捨てる] F --> J[CPUが自動でディスカード/ノック] I --> D J --> D H --> K{ジン?} K -->|はい| L[ラウンド終了 - スコア計算] K -->|いいえ| M[相手がレイオフ] M --> L L --> N{ポイント上限到達?} N -->|いいえ| B N -->|はい| O[マッチ終了 - 結果表示] O -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `drawstock` | `ds` | 山札から1枚ドロー | | `drawdiscard` | `dd` | 捨て札の山から1枚ドロー | | `discard ` | `d ` | カードを捨てる(インデックス指定) | | `knock` | `k` | ノック(デッドウッド≤10の時) | | `layoff ` | `lo ` | レイオフ(相手のメルドにカードを付け足す) | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `setlimit ` | `sl ` | ポイント上限設定 | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `ds` → 山札から1枚ドロー - `dd` → 捨て札の山からトップカードをドロー - `d 3` → インデックス3のカードを捨てる - `k` → ノック(デッドウッド≤10の時) - `lo 0 3` → インデックス0, 3のカードをレイオフ ## 画面の見方 ``` ========== Gin Rummy (ジンラミー) ========== ラウンド 2 デッドウッド上限: 10 ---------- [You]: 45点 [0]SPADE 3 [1]CLOVER 4 [2]CLOVER 5 [3]HEART 9 [4]DIAMOND J ... メルド: {♣4, ♣5, ♣6} デッドウッド: 19点 CPU: 30点 ---------- 捨て札トップ: HEART 7 山札残り: 22枚 手番: あなた ds・・・山札からドロー dd・・・捨て札からドロー ========== ``` - `ラウンド N`: 現在のラウンド番号 - `[You]: N点`: プレイヤーの累計得点 - `[0]SPADE 3`...: インデックス付きの手札カード - `メルド`: 現在認識されているメルドの組み合わせ - `デッドウッド: N点`: メルドに含まれないカードの合計点 - `捨て札トップ`: 捨て札の山の一番上のカード - `山札残り`: 山札の残り枚数 ## 遊び方のコツ - 序盤は捨て札からドローして、メルドを素早く作りましょう - 相手がどのカードを捨てているか注意し、相手のメルドを推測しましょう - デッドウッドの高いカード(J/Q/K=10点)は早めに処理するのが有効です - ジン(デッドウッド0)を狙えるなら、ノックを待ってボーナスを獲得しましょう - 相手のノックに備えて、レイオフできるカード(相手のメルドに付け足せるカード)を意識しておきましょう # ゴーフィッシュ(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。相手にカードのランク(数字)を要求し、同じランク4枚を揃えて「ブック」を作ります。最も多くブックを集めたプレイヤーの勝ちです。 ## 起動方法 ```sh go run ./cmd/trumpcards gofish go run ./cmd/trumpcards --lang en gofish # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 各プレイヤーに5枚ずつ配布、残りは山札 - 自分のターンに、手札に持っているランクを指定して相手に要求する - 相手がそのランクのカードを持っていれば、全て受け取り、もう一度要求できる - 相手が持っていなければ「Go Fish」 → 山札から1枚引く - 引いたカードが要求したランクならもう一度要求できる - 異なるランクなら次のプレイヤーのターンへ - 同じランク4枚が揃うと「ブック」として場に出す - 全13ブックが完成するか、山札がなくなりどのプレイヤーも行動できなくなったらゲーム終了 - 最も多くブックを持つプレイヤーの勝ち ### 得点 - ブック1つ = 1点 - 最多ブック数のプレイヤーが勝利 ### CPU難易度 | 難易度 | コマンド | 説明 | |--------|----------|------| | Easy | `sd 0` | ランダムに要求 | | Normal | `sd 1` | 手札のランクのみ要求、直近の記憶に基づいて判断 | | Hard | `sd 2` | 全要求履歴から最適な相手とランクを推測 | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 + 初期ブックチェック] B --> C[CPUが自動で手番を進行] C --> D{プレイヤーの手番?} D -->|はい| E[プレイヤーがランクを要求] D -->|いいえ| F[CPUが自動で要求] F --> D E -->|ask target rank| G{相手が持っている?} G -->|はい| H[カードを受け取る → ブックチェック] H --> I[もう一度要求可能] I --> E G -->|いいえ| J[Go Fish: 山札から1枚引く] J --> K{引いたカードが要求ランク?} K -->|はい| L[もう一度要求可能] L --> E K -->|いいえ| M[次のプレイヤーへ] M --> C H --> N{ゲーム終了?} N -->|はい| O[結果表示] N -->|いいえ| I O -->|reset| B ``` ## コマンド一覧 ### 基本コマンド | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始(現在の設定を維持) | | `ask ` | `a ` | 指定プレイヤーに指定ランクを要求(例: `ask 1 7`) | | `log` | `l` | 棋譜(アクションログ)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### 設定コマンド | コマンド | 短縮形 | 説明 | |----------|--------|------| | `sd N` | — | CPU難易度変更(0=Easy, 1=Normal, 2=Hard) | > **Note**: `target` はプレイヤーインデックス(1, 2, 3のいずれか)、`rank` はカードのランク(1=A, 2-10, 11=J, 12=Q, 13=K)です。 ## 画面の見方 ``` ========== Go Fish (ゴーフィッシュ) ========== 山札: 32枚 [You]: 5枚 | ブック: 0 [0]SPADE 3 [1]HEART 5 [2]DIAMOND 7 [3]CLOVER 7 [4]HEART K CPU 1: 5枚 | ブック: 0 CPU 2: 4枚 | ブック: 1 CPU 3: 5枚 | ブック: 0 ---------- あなたがCPU 1に7を要求 → 成功! 1枚受け取り [CPUの行動] CPU 1がCPU 2にKを要求 → Go Fish! CPU 2があなたに3を要求 → 成功! 1枚受け取り 手番: あなた ========== ``` - `山札: N枚`: 山札の残り枚数 - `[You]: N枚 | ブック: M`: プレイヤーの手札枚数とブック数 - `CPU N: M枚 | ブック: K`: CPUの手札枚数とブック数 - `[0]`, `[1]`...: 手札カードの参照(ランク確認用) - `成功!`: 相手がランクを持っていた場合 - `Go Fish!`: 相手がランクを持っていなかった場合 ## 遊び方のコツ - 手札に2枚以上持っているランクを要求すると、ブック完成に近づきやすい - CPUの要求を観察して、どのランクを持っているか推理しましょう - `log` コマンドで過去の要求履歴を確認し、誰がどのランクを持っているか推測できます - Hard難易度のCPUは全要求履歴を分析して最適な要求をするため、手強い対戦相手です # ゴルフ(CUI版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、7列×5段に並べた35枚の表向きカードから、ウェイストのトップカードと±1のランクのカードを除去していきます。タブローのカードをすべて除去すればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards golf go run ./cmd/trumpcards --lang en golf # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 7列×5段に35枚をすべて表向きに配る - 残りの17枚のうち1枚をウェイストに、16枚を山札(ストック)に置く - 各列の一番下の未除去カードのみ選択可能(露出カード) ### 除去ルール - ウェイストのトップカードと±1のランクのカードを除去できる - K-Aは繋がる(ラップアラウンド):KからAへ、AからKへ除去可能 - 例:ウェイストが5なら、4または6を除去できる ### ゲームクリア - タブローの35枚すべてを除去するとゲームクリア ### ゲームオーバー - これ以上除去可能な手がない場合はギブアップでゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札が空の場合に「手詰まりです」と表示します - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カードを配る] B --> C{除去可能な手がある?} C -->|はい| D{プレイヤーのアクション} D -->|draw| E[山札からウェイストにカードを引く] D -->|rm| F[カードを除去] D -->|hint| G[ヒントを表示] D -->|undo| H[直前の操作を取り消す] E --> C F --> I{タブロー全除去?} G --> C H --> C I -->|いいえ| C I -->|はい| J[ゲームクリア] C -->|いいえ| K[ギブアップ → ゲームオーバー] J -->|reset| A K -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `draw` | `d` | 山札からウェイストにカードを引く | | `remove ` | `rm` | 指定列のカードを除去(ウェイストトップ±1) | | `giveup` | `g` | ギブアップしてゲームを終了 | | `hint` | `h` | 次の一手のヒントを表示 | | `undo` | `u` | 直前の操作を元に戻す | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `d` → 山札からカードを引く - `rm 0` → 1列目のカードを除去 - `rm 3` → 4列目のカードを除去 - `u` → 直前の操作を元に戻す - `h` → ヒントを表示 ## 画面の見方 ``` ========== Golf (ゴルフ) ========== 山札: 16枚 ウェイスト: [♠5] ---------- タブロー: ♥A ♦K ♣7 ♠3 ♥8 ♦2 ♣9 ♠3 ♥8 ♦2 ♣9 ♠6 ♥Q ♦4 ♦4 ♣J ♠10 ♥3 ♦7 ♣6 ♠2 ♣5 ♠K ♥7 ♦A ♣3 ♠Q ♥6 (0)♣5 (1)♠K (2)♥7 (3)♦A (4)♣3 (5)♠Q (6)♥6 ---------- 手数: 0 rm・・・カード除去 d・・・山札を引く h・・・ヒント ========== ``` - `山札: N枚`: 山札の残りカード数 - `ウェイスト: [♠5]`: ウェイストの一番上のカード - 各列の一番下のカード `(N)` が操作可能な露出カード - `手数: N`: 現在の手数 ## 遊び方のコツ - ウェイストのトップカードから連続して除去できるチェーンを狙いましょう - K-Aのラップアラウンドを活用して長いチェーンを作りましょう - カードが多く残っている列を優先的に除去すると効率的です - 山札を引く前に、タブロー内で除去できるカードがないか確認しましょう - ヒントコマンドで詰まった時のアドバイスを得られます - アンドゥを活用して、異なる戦略を試しましょう # ハーツ(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。ハート(各1点)とQ♠(13点)を取らないようにし、誰かが100点以上になった時点で最も点数の低いプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli hearts ``` ## ルール ### 基本ルール - 52枚のトランプを4人に配布(1人13枚) - 各ラウンド開始時に3枚のカードをパスする(左→右→向かい→パスなし のローテーション) - 2♣を持っているプレイヤーが最初のトリックをリードする - リードされたスートに従う(フォロー必須)。なければ任意のカードを出せる - トリックの最高ランクのカード(リードスート)を出したプレイヤーがトリックを取る - ハートはブレイクされるまでリードできない(ハートブレイク) ### 得点 - ハート(♥)各1点(計13点) - スペードのクイーン(Q♠)13点 - 1ラウンドの合計は26点 ### シュート・ザ・ムーン - 1人のプレイヤーが26点すべてを取った場合、そのプレイヤーは0点、他の3人に+26点が加算される ### オムニバス・ハーツ(オプション) - J♦を獲得すると-10点(プレイヤーにとって有利) - シュート・ザ・ムーンのしきい値は16点(26 - 10)に変更される - J♦は最初のトリックでポイントカードとして扱われる(出せない) - CPUはJ♦を保持し、パスで渡さない戦略を取る ### ゲーム終了 - いずれかのプレイヤーが100点(デフォルト)以上に達した時点でゲーム終了 - 最も点数の低いプレイヤーが勝者 ### CPU難易度 - **Easy** (0): ランダムにカードを出す - **Normal** (1): ポイントカード(ハート・Q♠)を避ける戦略(デフォルト) - **Hard** (2): 戦略的なプレイ(ボイド作り、カウンティング等) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各13枚] B --> C{パスフェーズ?} C -->|はい| D[3枚のカードをパス] C -->|いいえ - パスなしラウンド| E[プレイフェーズ開始] D --> E E --> F[2♣がリード] F --> G{プレイヤーの手番?} G -->|はい| H[カードを選んで出す] G -->|いいえ| I[CPUが自動でカードを出す] H --> J[トリック完了] I --> J J --> K{13トリック終了?} K -->|いいえ| G K -->|はい| L[ラウンド終了 - 得点計算] L --> M{100点以上のプレイヤーあり?} M -->|いいえ| B M -->|はい| N[ゲーム終了 - 勝者表示] N -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `pass ` | — | 3枚のカードをパス(手札インデックスを3つ指定) | | `play ` | `p ` | 手札のカードを1枚出す(インデックス指定) | | `next` | `n` | 次のトリックへ進む | | `nextround` | `nr` | 次のラウンドへ進む | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度を設定(0=Easy, 1=Normal, 2=Hard) | | `setlimit ` | `sl ` | ポイント上限を設定 | | `hint` | `h` | 推奨カードのヒントを表示(パス・プレイフェーズで使用可) | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `pass 0 3 7` → インデックス0, 3, 7のカードをパスする - `p 2` → インデックス2のカードを出す - `sd 2` → CPU難易度をHardに設定 - `sl 50` → ポイント上限を50に設定 - `h` → 推奨カードのヒントを表示 ## 画面の見方 ``` ========== Hearts (ハーツ) ========== ラウンド 3 / トリック 5 パス方向: 左 ハートブレイク: はい ---------- [You]: 26点 (今ラウンド: 3点) [0]SPADE 2 [1]CLOVER 5 [2]HEART 9 [3]DIAMOND 11 ... CPU 1: 18点 (今ラウンド: 0点) CPU 2: 31点 (今ラウンド: 13点) CPU 3: 12点 (今ラウンド: 0点) ---------- 現在のトリック: CPU 1: CLOVER 10 CPU 2: CLOVER K 手番: あなた p ・・・カードを出す ========== ``` - `ラウンド N / トリック M`: 現在のラウンドとトリック番号 - `パス方向`: 現在のラウンドのパス方向(左/右/向かい/なし) - `ハートブレイク`: ハートがブレイクされたかどうか - `[You]: N点 (今ラウンド: M点)`: プレイヤーの累計得点と今ラウンドの得点 - `[0]SPADE 2`...: インデックス付きの手札カード - `現在のトリック`: 現在のトリックに出されたカード ## 遊び方のコツ - Q♠を持っている場合は、早めにスペードの高いカードを処理しておきましょう - ハートや高いカードを避けるために、特定のスートをボイド(手札からなくす)にする戦略が有効です - パスフェーズでは、Q♠やハートの高いカード、特定スートの残り枚数が少ないカードをパスすると良いでしょう - シュート・ザ・ムーンを狙う場合は、高いカードを多く保持し、すべてのポイントカードを確実に取れる手札構成が必要です - 他のプレイヤーがシュート・ザ・ムーンを狙っている兆候があれば、ポイントカードを1枚でも取って阻止しましょう - 迷った時は `h` コマンドでヒントを表示できます。CPUのHard戦略に基づく推奨カードが表示されます # テキサスホールデム(CUI版)遊び方 ## ゲーム概要 テキサスホールデムポーカーです。テーブルサイズは4-max(デフォルト、1人+CPU 3人)、6-max(1人+CPU 5人)、9-max(1人+CPU 8人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。GTOスタイルはボードテクスチャ分析に基づく混合戦略で意思決定します。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%)が全プレイヤーに表示され、CPUのベットサイズはポット相対(半額~全額)で決定されます。トーナメントモードでは、指定ハンド数ごとにブラインドが自動的に上昇します。リバイ(チップ再購入)とアドオン(一回限りのチップ追加購入)にも対応しています。 ## 起動方法 ```sh go run ./cmd/cli holdem ``` ## ルール ### 基本ルール - 52枚のトランプを使用 - 各プレイヤーに2枚のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - ホールカード2枚 + コミュニティカード5枚の計7枚から、最強の5枚の組み合わせで役を作る - 最も強い役を持つプレイヤーがポットを獲得する ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード2枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 5. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 6. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 7. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 8. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | Full House | 同じ数字3枚 + 同じ数字2枚 | | Flush | 同じスート5枚 | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略。ドライボードでは2/3ポット、ウェットボードでは3/4ポットのベットサイズ | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ブラインド投入] B --> C[ホールカード2枚配布] C --> D[プリフロップ - ベッティング] D --> E{全員アクション完了?} E -->|いいえ| D E -->|はい| F[フロップ - 3枚公開] F --> G[ベッティング] G --> H{全員アクション完了?} H -->|いいえ| G H -->|はい| I[ターン - 4枚目公開] I --> J[ベッティング] J --> K{全員アクション完了?} K -->|いいえ| J K -->|はい| L[リバー - 5枚目公開] L --> M[ベッティング] M --> N{全員アクション完了?} N -->|いいえ| M N -->|はい| O[ショーダウン] O --> P[勝者決定・チップ配分] P --> P2{プレイヤーが負けた?} P2 -->|はい| P3[マック/ショー選択] P3 --> Q{リバイ/アドオン対象?} P2 -->|いいえ| Q{リバイ/アドオン対象?} Q -->|はい| R[リバイ/アドオン選択] R -->|reset| A Q -->|いいえ| S[次のハンドへ] S -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `fold` | `f` | フォールド(降りる) | | `check` | `ck` | チェック(パス) | | `call` | `c` | コール(ベット額に合わせる) | | `bet <額>` | `b <額>` | ベットする | | `raise <額>` | `ra <額>` | レイズする | | `allin` | `a` | オールイン | | `bl [0-2]` | | ベッティングリミット変更(0=Fixed, 1=PotLimit, 2=NoLimit) | | `ts [4\|6\|9]` | `tablesize` | テーブルサイズ変更(4-max, 6-max, 9-max) | | `rebuy` | `rb` | リバイ(チップを再購入) | | `skiprebuy` | `sr` | リバイをスキップ | | `addon` | `ad` | アドオン(チップを追加購入) | | `skipaddon` | `sa` | アドオンをスキップ | | `muck` | `m` | マック(手札を伏せる) | | `show` | `sh` | ショー(手札を公開する) | | `metaai` | `mai` | メタAIの ON/OFF を切り替え(CPUが人間のプレイスタイルを学習) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | 例: - `b 40` → 40チップをベット - `ra 80` → 80チップにレイズ - `c` → コール ## 画面の見方 ``` ========== Texas Hold'em ========== ディーラー: Player 0 コミュニティ: ♠7 ♣10 ♥3 ポット: 60 ---------- [You] チップ:970 ベット:20 手札: ♠1 ♥13 CPU 1 (TAG) チップ:980 ベット:20 CPU 2 (LAP) チップ:990 ベット:10 [フォールド] CPU 3 (GTO) チップ:960 ベット:10 ---------- [CPU行動] Player 1: コール Player 2: フォールド Player 3: チェック ========== ``` - `[You]`: プレイヤーのチップ残高とベット額 - `手札`: あなたのホールカード - `CPU N (スタイル)`: CPUのチップ残高、ベット額、状態 - `[フォールド]`: フォールド済み - `[オールイン]`: オールイン状態 - `コミュニティ`: 場に公開されたコミュニティカード - `ポット`: 現在のポット額 - `[CPU行動]`: CPUが行ったアクションの記録 ## HUD統計 各プレイヤーにはHUD(ヘッズアップディスプレイ)統計が表示されます: - **VPIP** (Voluntarily Put $ In Pot): プリフロップで自発的にチップを入れた割合(コール/ベット/レイズ/オールイン) - **PFR** (Pre-Flop Raise): プリフロップでレイズした割合(ベット/レイズ/オールイン) ## トーナメントモード トーナメントモードを有効にすると、指定ハンド数ごとにブラインドが自動的に上昇します。 - **BlindLevelHands**: ブラインドレベルアップまでのハンド数(デフォルト10) - **BlindMultiplier**: ブラインド倍率(デフォルト200 = 2倍) 例: BlindLevelHands=10, BlindMultiplier=200 の場合、10ハンドごとにSB/BBが2倍になります。 ## リバイ/アドオン リバイとアドオンを有効にすると、チップが不足した際や特定のハンド数経過後にチップを追加購入できます。 ### リバイ - リバイ期間内(デフォルト: 最初の10ハンド)にチップが0になった場合、リバイが可能 - 最大リバイ回数(デフォルト3回)まで繰り返しリバイ可能 - リバイで得られるチップ数はデフォルト1000 - CPUは自動的にリバイを実行 ### アドオン - 指定ハンド数(デフォルト: 10ハンド目)経過後に1回だけアドオン可能 - アドオンで得られるチップ数はデフォルト1500 - CPUは自動的にアドオンを実行 ## 遊び方のコツ - ポジション(ディーラーからの距離)は重要です。後ろのポジションほど有利です - 強い手(ペア、高いカード)はプリフロップでレイズして主導権を握りましょう - LAGスタイルのCPUはブラフが多いので、良い手を持っているときはコールやレイズで対抗しましょう - GTOスタイルのCPUは確率的な混合戦略を使うため、行動パターンが読みにくいです。ボードテクスチャに注意して対応しましょう - TAGスタイルのCPUがレイズしてきたら、本当に強い手を持っている可能性が高いので注意しましょう - HUD統計を活用して、CPUのプレイスタイルの傾向を把握しましょう - トーナメントモードではブラインドが上がるため、早めにチップを稼ぐ戦略が重要です - オールインは最後の手段として使いましょう。全チップを失うリスクがあります # インディアンポーカー(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶベッティング型カードゲームです。各プレイヤーに1枚ずつカードが配られ、自分のカードは見えず相手のカードだけが見える状態でベッティングを行います。カードのランクが最も高いプレイヤーがポットを獲得します。 ## 起動方法 ```sh go run ./cmd/trumpcards indianpoker go run ./cmd/trumpcards --lang en indianpoker # 英語モード ``` ## ルール ### 基本ルール - 52枚のカードから各プレイヤーに1枚ずつ配布 - 自分のカードは見えない(額に貼り付けるイメージ) - 他のプレイヤーのカードは見える - カードのランクで勝敗を決定(2が最弱、Ace=14が最強) - 各ラウンド開始時にアンティ(参加費)を投入 ### ベッティング - **フォールド**: 降りる(投入済みチップは失う) - **チェック**: パスする(未ベット時のみ) - **コール**: 現在のベット額に合わせる - **ベット**: 最初の賭け金を出す - **レイズ**: 現在のベット額を引き上げる - **オールイン**: 全チップを賭ける ### ベッティングリミット - **Fixed**: 固定額ベット - **Pot Limit**: ポット額まで - **No Limit**: 上限なし(デフォルト) ### ゲーム終了 - いずれかのプレイヤーのチップが0になった場合にゲーム終了 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[アンティ投入・カード配布] B --> C[ベッティングラウンド] C --> D{プレイヤーの手番?} D -->|はい| E[アクション選択: fold/check/call/bet/raise/allin] D -->|いいえ| F[CPUが自動でアクション] E --> G{ベッティング終了?} F --> G G -->|いいえ| D G -->|はい| H[ショーダウン - カード公開・勝者決定] H --> I{ゲーム終了条件を満たした?} I -->|いいえ| B I -->|はい| J[ゲーム終了 - 結果表示] J -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット | | `fold` | `f` | フォールド(降りる) | | `check` | `ck` | チェック(パス) | | `call` | `c` | コール(ベット額に合わせる) | | `bet ` | `b ` | ベット(金額指定) | | `raise ` | `ra ` | レイズ(金額指定) | | `allin` | `a` | オールイン | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `b 50` → 50チップをベット - `ra 100` → 100チップにレイズ - `c` → 現在のベットにコール - `f` → フォールド ## 画面の見方 ``` ========== Indian Poker (インディアンポーカー) ========== ハンド #5 | ディーラー: CPU 1 ポット: 120 | アンティ: 10 ---------- [You]: 850チップ (ベット: 30) [カード: ???] CPU 1: 920チップ (ベット: 30) [カード: HEART K] CPU 2: 680チップ (フォールド) [カード: SPADE 5] CPU 3: 550チップ (ベット: 30) [カード: DIAMOND 10] ---------- 手番: あなた f・・・フォールド ck・・・チェック c・・・コール b ・・・ベット ra ・・・レイズ a・・・オールイン ========== ``` - `ハンド #N`: 現在のハンド番号 - `ディーラー`: 現在のディーラー - `ポット`: ポットの合計チップ - `[カード: ???]`: 自分のカードは見えない - `[カード: HEART K]`: 他プレイヤーのカードは見える - `(ベット: N)`: 現在のベット額 - `(フォールド)`: フォールド済み ## 遊び方のコツ - 他のプレイヤーのカードを観察し、自分のカードの強さを推測しましょう - 相手のカードが弱い場合、自分のカードが強い可能性が高いのでベットしましょう - CPUのベッティングパターンから自分のカードの強さを推測できます - オールインは慎重に。チップが0になるとゲーム終了です - メタAIが有効な場合、CPUはあなたのプレイパターンを学習して戦略を調整します # ジョーカーポーカー(CUI版)遊び方 ## ゲーム概要 1人用カジノカードゲームです。53枚のトランプ(52枚+ジョーカー1枚)を使い、5枚のカードでポーカーの役を作ります。ジョーカーがワイルドカードとして扱われ、他の任意のカードの代わりになります。Joker Poker専用のペイテーブルに基づいて配当が支払われます。 ## 起動方法 ```sh go run ./cmd/trumpcards jokerpoker go run ./cmd/trumpcards --lang en jokerpoker # 英語モード ``` ## ルール ### 基本ルール - 53枚のトランプ(52枚+ジョーカー1枚)を使用 - ジョーカーがワイルドカード - 1〜5コインをベットしてゲーム開始 - 5枚のカードが配られる - 残したいカード(ホールド)を選び、残りを交換 - 最終的な5枚の手札で役を判定し、配当を支払い ### 配当表(Joker Poker) | 役 | 1コイン | 2コイン | 3コイン | 4コイン | 5コイン | |----|---------|---------|---------|---------|---------| | ナチュラルロイヤルフラッシュ | 250 | 500 | 750 | 1000 | 4000 | | ファイブカード | 200 | 400 | 600 | 800 | 1000 | | ワイルドロイヤルフラッシュ | 100 | 200 | 300 | 400 | 500 | | ストレートフラッシュ | 50 | 100 | 150 | 200 | 250 | | フォーカード | 20 | 40 | 60 | 80 | 100 | | フルハウス | 7 | 14 | 21 | 28 | 35 | | フラッシュ | 5 | 10 | 15 | 20 | 25 | | ストレート | 3 | 6 | 9 | 12 | 15 | | スリーカード | 2 | 4 | 6 | 8 | 10 | | ツーペア | 1 | 2 | 3 | 4 | 5 | | キングスオアベター | 1 | 2 | 3 | 4 | 5 | ### ワイルドカード - ジョーカーは任意のカードとして扱える - ナチュラルロイヤルフラッシュ: ワイルドなしのロイヤルフラッシュ(最高配当) - ワイルドロイヤルフラッシュ: ジョーカーを含むロイヤルフラッシュ - ファイブカード: ジョーカーを使って同じランク5枚 ### キングスオアベター - K、Aのワンペア以上が配当の最低条件 - Q以下のワンペアは配当なし(ツーペアは配当あり) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ベットフェーズ] B --> C[b コイン数 でベット] C --> D[5枚のカードが配られる] D --> E[ドローフェーズ] E --> F[h インデックス でホールドするカードを選択] F --> G[残りのカードを交換] G --> H[役判定・配当] H -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `bet ` | `b ` | ベットする(1〜5コイン) | | `hold ` | `h ` | カードをホールドして交換(インデックス0〜4) | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `r` → ゲームリセット - `b 5` → 5コインベット - `h 0 2 4` → 0番目、2番目、4番目のカードをホールドし、残りを交換 - `h` → 全カード交換(ホールドなし) - `l` → 棋譜を表示 ## 画面の見方 ``` ========== ジョーカーポーカー ========== チップ: 100 ---------- ベットフェーズ b・・・ベット l・・・棋譜 ========== > b 3 ========== ジョーカーポーカー ========== チップ: 97 ---------- [0]♠A [1]★JK [2]♦K [3]♣3 [4]♠J ---------- ドローフェーズ h・・・ホールド l・・・棋譜 ========== > h 0 1 2 ========== ジョーカーポーカー ========== チップ: 103 ---------- [0]♠A [1]★JK [2]♦K [3]♥A [4]♦9 ---------- スリーカード 配当: 6 ---------- r・・・リセット l・・・棋譜 ========== ``` - `チップ: N`: 所持チップ数 - `[N]♠A`: カードのインデックスとカード表示 - `★JK`: ジョーカー - `配当: N`: 獲得配当 ## 遊び方のコツ - 5コインベットでナチュラルロイヤルフラッシュの配当が大幅に増える(250→4000)ため、可能なら最大ベットがお得 - ジョーカーは常にホールドする - ジョーカーを持っている場合、スリーカード以上を狙いやすい - ジョーカーなしの場合はキングスオアベター(K以上のペア)が最低配当条件なので注意 - ツーペアも配当があるため、ペアが2つある場合はホールドする # クロンダイク(CUI版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、7列のタブロー、山札、ウェスト、4つの組札で構成されます。組札にA→Kまでスート別に積み上げればクリアです。 ## 起動方法 ```sh go run ./cmd/cli klondike ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 7列のタブローに左から1枚、2枚、...、7枚ずつカードを配る(各列の一番上のカードのみ表向き) - 残りの24枚は山札(ストック)に裏向きで置く - 4つの組札(ファンデーション)はスート別にA→Kの順に積み上げる - タブローでは降順かつ交互の色で重ねる(例: 黒のKの上に赤のQ) ### ゲームクリア - 4つの組札すべてにA→Kまで13枚ずつ積み上げるとゲームクリア ### ゲームオーバー - これ以上移動可能な手がない場合はゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札とウェストが空の場合、または山札を一巡しても進展がなかった場合に「手詰まりです」と表示します - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カードを配る] B --> C{移動可能な手がある?} C -->|はい| D{プレイヤーのアクション} D -->|draw| E[山札からウェストにカードをめくる] D -->|move| F[カードを移動] D -->|hint| G[ヒントを表示] D -->|autocomplete| H[自動で組札に積み上げ] E --> C F --> I{組札が完成?} G --> C H --> I I -->|いいえ| C I -->|はい| J[ゲームクリア] C -->|いいえ| K[ゲームオーバー] J -->|reset| A K -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `reset 3` | | 3枚引きモードで新しいゲームを開始 | | `reset 1` | | 1枚引きモードで新しいゲームを開始 | | `draw` | `d` | 山札からウェストにカードをめくる(1枚または3枚) | | `move` | `m` | カードを移動する(サブ引数で移動元・移動先を指定) | | `undo` | `u` | 直前の操作を元に戻す | | `giveup` | `g` | ギブアップしてゲームを終了 | | `hint` | `h` | 次の一手のヒントを表示 | | `autocomplete` | `ac` | 自動的に組札へ積み上げ可能なカードを移動 | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `d` → 山札からカードをめくる - `m` → 移動コマンド(移動元・移動先の入力を求められる) - `u` → 直前の操作を元に戻す - `h` → ヒントを表示 - `ac` → オートコンプリート - `reset 3` → 3枚引きモードでリスタート ## 画面の見方 ``` ========== Klondike (ソリティア) ========== 山札: 20枚 ウェスト: ♠K ---------- 組札: [♠] A [♥] A 2 [♦] -- [♣] -- ---------- タブロー: [1] ?? ♥Q [2] ?? ?? ♣J [3] ?? ?? ?? ♦10 [4] ?? ?? ?? ?? ♠9 [5] ?? ?? ?? ?? ?? ♥8 [6] ?? ?? ?? ?? ?? ?? ♣7 [7] ?? ?? ?? ?? ?? ?? ?? ♦6 ---------- 手数: 15 m・・・カードを移動 d・・・山札をめくる h・・・ヒント ========== ``` - `山札: N枚`: 山札の残りカード数 - `ウェスト: ♠K`: ウェストの一番上のカード - `組札`: 各スートの組札の状態(`--`は空) - `??`: 裏向きのカード - `♥Q`: 表向きのカード(ハートのQ) - `手数: N`: 現在の手数 ## 遊び方のコツ - まずタブローの裏向きカードをめくることを優先しましょう - Aが出たらすぐに組札へ移動しましょう - 空いた列にはKのみ置くことができます - 山札を何度もめくり直して使えるカードを探しましょう - ヒントコマンドで詰まった時のアドバイスを得られます - オートコンプリートは組札に安全に移動できるカードがある場合に便利です # 神経衰弱(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。52枚のカードを裏向きに並べ、2枚ずつめくって同じランクのペアを揃えます。全26ペアが取られた時点で最もペア数の多いプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli memory ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を裏向きにボード上に並べる - プレイヤーは順番に2枚のカードをめくる - めくった2枚が同じランクならペアとして獲得し、もう1ターン続行 - 異なるランクなら2枚を裏に戻し、次のプレイヤーに交代 ### ゲーム終了 - 全26ペアが取られた時点でゲーム終了 - 最もペア数の多いプレイヤーが勝者 ### CPU難易度 - **Easy** (0): ランダムにカードをめくる - **Normal** (1): 部分的にカードの位置を記憶する(デフォルト) - **Hard** (2): 完全にカードの位置を記憶する ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[52枚のカードを裏向きに配置] B --> C{プレイヤーの手番?} C -->|はい| D[1枚目のカードをめくる] C -->|いいえ| E[CPUが自動でカードをめくる] D --> F[2枚目のカードをめくる] F --> G{同じランク?} E --> G G -->|はい| H[ペアを獲得] H --> I{全ペア取得済み?} I -->|いいえ| C G -->|いいえ| J[カードを裏に戻す - next] J --> K{全ペア取得済み?} K -->|いいえ| L[次のプレイヤーへ] L --> C I -->|はい| M[ゲーム終了 - 勝者表示] K -->|はい| M M -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `flip ` | `f ` | 指定位置のカードをめくる | | `next` | `n` | 結果確認後に次へ進む | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度を設定(0=Easy, 1=Normal, 2=Hard) | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `f 5` → 位置5のカードをめくる - `flip 12` → 位置12のカードをめくる - `sd 2` → CPU難易度をHardに設定 ## 画面の見方 ``` ========== Memory (神経衰弱) ========== 残りペア: 20 / 26 ---------- [You]: 3ペア CPU 1: 2ペア CPU 2: 1ペア CPU 3: 0ペア ---------- ボード: [ 0]?? [ 1]?? [ 2]?? [ 3]♠2 [ 4]?? [ 5]?? [ 6]-- [ 7]?? [ 8]?? [ 9]?? [10]?? [11]?? [12]?? [13]-- ... 手番: あなた f ・・・カードをめくる ========== ``` - `残りペア: N / 26`: ボード上に残っているペア数 - `[You]: Nペア`: プレイヤーの獲得ペア数 - `[ 0]??`: 裏向きのカード(位置0) - `[ 3]♠2`: 表向きのカード(位置3、スペードの2) - `[ 6]--`: 取られたカード(位置6) ## 遊び方のコツ - めくられたカードの位置とランクを覚えておきましょう - CPUがめくったカードも注意深く観察すると、ペアの位置を特定しやすくなります - 序盤は情報収集を重視し、まだ見ていない位置のカードを優先的にめくると効率的です - 確実にペアだとわかるカードがあれば、優先的にめくりましょう # ナポレオン(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。52枚+ジョーカー1枚の計53枚を使用し、絵札(J/Q/K/A + ジョーカー = 最大17枚)の獲得数をビッドして競います。最高ビッドのプレイヤーがナポレオンとなり、切り札スートと副官カードを宣言します。ナポレオン軍(ナポレオン+副官)と連合軍の隠しチーム戦です。 ## 起動方法 ```sh go run ./cmd/trumpcards napoleon go run ./cmd/trumpcards --lang en napoleon # 英語モード ``` ## ルール ### 基本ルール - 53枚のカード(52枚+ジョーカー1枚)を4人に配布(1人13枚、余り1枚はキティ) - 各ラウンド開始時に絵札の獲得予想数をビッド(最低入札数〜17) - 最高ビッドのプレイヤーが **ナポレオン** になる - ナポレオンは **切り札スート** を宣言し、 **副官カード** を指名する - 副官カードを持つプレイヤーはナポレオンの味方(他のプレイヤーには非公開) - ナポレオンはキティのカードと手札を交換できる - リードスートに従う(フォロースート) - トリックの勝者: 切り札が出ていれば最高の切り札、なければリードスートの最高値 ### 特殊カード - **ジョーカー(マイティ)**: 最も強いカード。どのトリックでも勝つ - **スペードの3(ジョーカーキラー)**: ジョーカーに対してのみ勝てる特殊カード ### 絵札(ピクチャーカード) 以下のカードが絵札としてカウントされます(最大17枚): - ジャック (J) × 4枚 - クイーン (Q) × 4枚 - キング (K) × 4枚 - エース (A) × 4枚 - ジョーカー × 1枚 ### 得点 - **ナポレオン軍の勝利**: ナポレオン軍が獲得した絵札数 >= ビッド数 - **連合軍の勝利**: ナポレオン軍が獲得した絵札数 < ビッド数 ### ゲーム終了 - ポイント上限に到達したプレイヤーが出た場合にゲーム終了 ### CPU難易度 - **Easy** (0): ランダムにカードを出す - **Normal** (1): 基本的なトリック戦略(デフォルト) - **Hard** (2): 戦略的なプレイ(切り札管理等) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各13枚 + キティ1枚] B --> C[ビッドフェーズ] C --> D[各プレイヤーがビッドまたはパス] D --> E[ナポレオン決定] E --> F[切り札スート宣言] F --> G[副官カード指名] G --> H[ナポレオンがキティと手札を交換] H --> I{プレイヤーの手番?} I -->|はい| J[カードを選んで出す] I -->|いいえ| K[CPUが自動でカードを出す] J --> L[トリック完了] K --> L L --> M{13トリック終了?} M -->|いいえ| I M -->|はい| N[ラウンド終了 - 絵札集計・得点計算] N --> O{ゲーム終了条件を満たした?} O -->|いいえ| B O -->|はい| P[ゲーム終了 - 結果表示] P -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `bid ` | `b ` | ビッドを宣言(0=パス、最低入札数〜17) | | `trump ` | `t ` | 切り札スートを宣言(1=スペード, 2=クローバー, 3=ハート, 4=ダイヤ) | | `exchange ` | `e ` | キティとカードを交換(インデックス指定) | | `play ` | `p ` | カードをプレイ(インデックス指定) | | `next` | `n` | 次のトリックへ | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `hint` | `h` | 推奨カードのヒントを表示 | | `log` | `l` | 棋譜表示 | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `setlimit ` | `sl ` | ポイント上限設定 | | `setminbid ` | `sm ` | 最低入札数設定 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `b 12` → 絵札12枚をビッド - `b 0` → パス(ビッドしない) - `t 1` → スペードを切り札に宣言 - `e 0 3` → インデックス0と3のカードをキティと交換 - `p 2` → インデックス2のカードを出す - `sd 2` → CPU難易度をHardに設定 - `sl 300` → ポイント上限を300に設定 - `sm 10` → 最低入札数を10に設定 ## 画面の見方 ``` ========== Napoleon (ナポレオン) ========== ラウンド 2 / トリック 5 切り札: スペード ナポレオン: CPU 1 (ビッド: 12) 副官カード: HEART A ---------- [You]: 50点 (絵札: 3) [0]SPADE 5 [1]CLOVER 8 [2]HEART K [3]DIAMOND 3 ... CPU 1: 80点 (ナポレオン, 絵札: 5) CPU 2: 30点 (絵札: 2) CPU 3: 40点 (絵札: 1) ---------- 現在のトリック: CPU 1: CLOVER 10 CPU 2: CLOVER K 手番: あなた p ・・・カードを出す ========== ``` - `ラウンド N / トリック M`: 現在のラウンドとトリック番号 - `切り札`: 宣言された切り札スート - `ナポレオン`: ナポレオンのプレイヤーとビッド数 - `副官カード`: 指名された副官カード - `[You]: N点 (絵札: X)`: プレイヤーの累計得点と獲得絵札数 - `[0]SPADE 5`...: インデックス付きの手札カード - `現在のトリック`: 現在のトリックに出されたカード ## 遊び方のコツ - ビッドは手札の強さに応じて慎重に行いましょう。絵札とトランプの枚数を確認してからビッドしましょう - ナポレオンになった場合、切り札スートは自分が多く持つスートを選びましょう - 副官カードは、自分が持っていない強いカード(例: エース)を指名すると効果的です - ジョーカー(マイティ)は最強のカードですが、スペードの3(ジョーカーキラー)に注意しましょう - 連合軍の場合、ナポレオンの切り札を早く消費させる戦略が有効です - 迷った時は `h` コマンドでヒントを表示できます # オー・ヘル(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。ラウンドごとに手札枚数が変動し、各ラウンドでビッド(獲得予想トリック数)を宣言します。全ラウンド終了後、累計得点が最も高いプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/trumpcards ohhell go run ./cmd/trumpcards --lang en ohhell # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを4人に配布(手札枚数はラウンドごとに変動) - 配り終わった山札の一番上のカードが**切り札(トランプ)スート**を決定 - 山札が残らないラウンドでは切り札なし - 各ラウンド開始時にビッド(0〜手札枚数)を宣言 - **ディーラー制限(フック)**: ディーラーは全員のビッド合計が手札枚数と同じになるビッドを選べない - リードスートに従う(フォロースート) - トリックの勝者: 切り札が出ていれば最高の切り札、なければリードスートの最高値 ### 得点 - **ビッド成功(正確に一致)**: 10 + ビッド数 - **ビッド失敗**: 0点(スタンダード方式)または -|差分|点(ペナルティ方式) ### ラウンド進行 - **減少のみ**: 最大手札枚数 → 1枚 - **減少後増加**: 最大手札枚数 → 1枚 → 最大手札枚数 ### ゲーム終了 - 全ラウンド終了後、累計得点が最も高いプレイヤーが勝利 ### CPU難易度 - **Easy** (0): ランダムにビッド・カードを出す - **Normal** (1): 基本的なトリック戦略(デフォルト) - **Hard** (2): 戦略的なプレイ ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 手札枚数はラウンドにより変動] B --> C[切り札カード公開] C --> D[ビッドフェーズ] D --> E[各プレイヤーがビッド宣言] E --> F{プレイヤーの手番?} F -->|はい| G[カードを選んで出す] F -->|いいえ| H[CPUが自動でカードを出す] G --> I[トリック完了] H --> I I --> J{全トリック終了?} J -->|いいえ| F J -->|はい| K[ラウンド終了 - 得点計算] K --> L{全ラウンド終了?} L -->|いいえ| B L -->|はい| M[ゲーム終了 - 結果表示] M -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `bid ` | `b ` | ビッドを宣言(0〜手札枚数) | | `play ` | `p ` | カードをプレイ(インデックス指定) | | `next` | `n` | 次のトリックへ | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `setmaxhand ` | `sm ` | 最大手札枚数設定(1-13) | | `hint` | `h` | 推奨カード/ビッドのヒントを表示 | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `b 3` → ビッド3を宣言 - `b 0` → ビッド0を宣言 - `p 2` → インデックス2のカードを出す - `sd 2` → CPU難易度をHardに設定 - `sm 7` → 最大手札枚数を7に設定 - `h` → 推奨カード/ビッドのヒントを表示 ## 画面の見方 ``` ========== Oh Hell (オー・ヘル) ========== ラウンド 3/19 / トリック 2 (手札 8枚) 切り札: HEART (♥K) ---------- [You]: 30点 (ビッド: 2, 獲得: 1) [0]SPADE 5 [1]CLOVER 8 [2]HEART K [3]DIAMOND 3 ... CPU 1: 20点 (ビッド: 3, 獲得: 2) CPU 2: 40点 (ビッド: 1, 獲得: 1) CPU 3: 10点 (ビッド: 2, 獲得: 0) ---------- 現在のトリック: CPU 1: CLOVER 10 CPU 2: CLOVER K 手番: あなた p ・・・カードを出す ========== ``` - `ラウンド N/M / トリック T (手札 X枚)`: 現在のラウンド/全ラウンド数、トリック番号、手札枚数 - `切り札: SUIT (カード)`: 切り札スートと決定カード - `[You]: N点 (ビッド: X, 獲得: Y)`: プレイヤーの累計得点、ビッド数、獲得トリック数 - `[0]SPADE 5`...: インデックス付きの手札カード - `現在のトリック`: 現在のトリックに出されたカード ## 遊び方のコツ - ビッドは手札の強さ(切り札の枚数、高いカード)を考慮して慎重に宣言しましょう - ディーラーの場合はフック制限を意識してビッドしましょう - 切り札が少ないラウンドではビッドを控えめにするのが安全です - 手札枚数が少ないラウンドほど運の要素が強くなります - 正確なビッドが高得点の鍵です(多くても少なくてもダメ) - 迷った時は `h` コマンドでヒントを表示できます # ババ抜き(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。ペアを揃えて手札をなくしていき、最後にジョーカーを持っているプレイヤーが負けです。 ## 起動方法 ```sh go run ./cmd/cli oldmaid ``` ## ルール ### 基本ルール - 52枚のトランプ + ジョーカー1枚(合計53枚)を使用 - 全カードを4人に配布し、最初に手札内のペア(同じ数字2枚)を全て捨てる - 順番に次のプレイヤーの手札から1枚引き、ペアができたら捨てる - 手札がなくなったプレイヤーから上がり - 最後にジョーカーが残ったプレイヤーが負け ### ジジ抜きモード - ジョーカーを使わず、52枚から1枚をランダムに除外して51枚で遊ぶ - 除外されたカードの対となるカードが「奇数カード」(=ババ)となる - `sm 1` コマンドで切り替え可能 ### プレイ順 - 4人のプレイ順はラウンド開始時にランダムに決定されます - 上がったプレイヤーはスキップされます ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 + ペア除去] B --> C[CPUが自動で手番を進行] C --> D{プレイヤーの手番?} D -->|はい| E[プレイヤーがカードを引く] D -->|いいえ| F[CPUが自動でカードを引く] F --> D E -->|draw / draw N| G[ペアがあれば自動で捨てる] G --> H{ゲーム終了?} H -->|いいえ| C H -->|はい| I[結果表示 - 負けのプレイヤーを発表] I -->|reset| B ``` ## コマンド一覧 ### 基本コマンド | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始(現在の設定を維持) | | `draw` | `d` | 次のプレイヤーからランダムに1枚引く | | `draw N` | `d N` | 次のプレイヤーの手札からインデックスNのカードを引く(例: `d 2`) | | `shuffle` | `s` | 自分の手札をランダムに並べ替える | | `reorder i0 i1 ...` | `ro i0 i1 ...` | 自分の手札を指定した順番に並べ替える(例: `ro 2 0 1`) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### 設定コマンド | コマンド | 短縮形 | 説明 | |----------|--------|------| | `setmode N` | `sm N` | ゲームモード変更(0=ノーマル, 1=ジジ抜き) | | `setplacementstrategy N` | `sps N` | CPU心理戦の切替(0=OFF, 1=ON) | | `setmemoryai N` | `sma N` | CPU記憶AIの切替(0=OFF, 1=ON) | | `setmetaai N` | `smai N` | Meta-AI の切替(0=OFF, 1=ON) | | `resetprofile` | `rp` | Meta-AI のプロファイル(学習データ)をリセット | > **Note**: 設定コマンドを実行するとゲームがリセットされます。`r`(reset)コマンドは現在の設定を維持してリセットします。 ## CPU AI機能 ### CPU心理戦(`sps 1`で有効化) CPUが人間プレイヤーのドロー対象となった際に、奇数カード(ジョーカーまたはペアにならないカード)を手札の端(先頭か末尾)に配置します。端のカードを引く傾向がある場合に有利になる心理戦略です。 ### CPU記憶AI(`sma 1`で有効化) CPUが人間プレイヤーから引く際に、過去の引き位置を記憶して戦略的にカードを選択します: - 前回引いた位置でペアができた場合、その近傍(±1)を優先 - 前回引いた位置でペアができなかった場合、その位置を回避 - 人間が手札をシャッフルまたは並べ替えた場合、記憶をリセット ## 画面の見方 ``` ========== Old Maid (ババ抜き) ========== [You]: 12枚 [0]SPADE 3 [1]HEART 5 [2]DIAMOND 9 ... CPU 1: 13枚 CPU 2: 上がり CPU 3: 12枚 ---------- あなたがCPU 1から1枚引きました (HEART 9)。1組捨てました [CPUの行動] CPU 1がCPU 3から1枚引きました。2組捨てました ... [引き履歴] 1. Player 1 → CPU 1: 1組捨て 2. CPU 1 → CPU 3: 2組捨て 手番: あなた → CPU 1から引きます ========== ``` - `[You]: N枚`: プレイヤーの手札枚数(カード内容も表示) - `CPU N: M枚`: CPUの手札枚数(カード内容は非表示) - `上がり`: そのプレイヤーは手札がなくなり終了 - `[0]`, `[1]`...: カードのインデックス(`draw N` で指定可能) - `手番: あなた → CPU Nから引きます`: 誰から引くかの案内 - `[引き履歴]`: ゲーム全体の引きの履歴(番号付き、誰が誰から引いたか・捨てたペア数・上がりフラグを表示) ## 遊び方のコツ - `draw`(ランダム)でも `draw N`(インデックス指定)でもカードを引けます - 相手の手札は裏向きなので、インデックス指定でも何が引けるかはわかりません - CPUの行動は自動で進行し、結果がログに表示されます - CPUは端のカードを優先して引く傾向があるので、`shuffle` や `reorder` で手札を並べ替え、ジョーカーを端から遠ざけると有利です - `reorder` コマンドで手札の順番を自由に指定できます(例: 手札が5枚の場合 `ro 4 3 2 1 0` で逆順に) # オマハホールデム(CUI版)遊び方 ## ゲーム概要 オマハホールデムポーカーです。テキサスホールデムの派生ゲームで、各プレイヤーに4枚のホールカードが配られます。テーブルサイズは4-max(デフォルト、1人+CPU 3人)、6-max(1人+CPU 5人)、9-max(1人+CPU 8人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%/3Bet%/AF)が全プレイヤーに表示され、トーナメントモードではブラインドが自動上昇します。リバイとアドオンにも対応しています。 ## 起動方法 ```sh go run ./cmd/trumpcards omaha go run ./cmd/trumpcards --lang en omaha # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを使用 - 各プレイヤーに**4枚**のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - **ホールカードから必ず2枚**、**コミュニティカードから必ず3枚**を使って5枚の役を作る(テキサスホールデムとの最大の違い) - 最も強い役を持つプレイヤーがポットを獲得する ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード4枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 5. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 6. **ショーダウン**: 残ったプレイヤーのハンドを比較 ### ベッティングリミット - **Fixed Limit** (0): レイズ額が固定 - **Pot Limit** (1): ポット額までレイズ可能(デフォルト) - **No Limit** (2): 全チップまでレイズ可能 ### ゲーム終了 - チップが0になったプレイヤーは脱落(トーナメントモードではリバイ可能) - 最後の1人になったらゲーム終了 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ブラインド強制ベット] B --> C[ホールカード4枚配布] C --> D[プリフロップ - ベッティング] D --> E{全員フォールド?} E -->|はい| K[ポット獲得] E -->|いいえ| F[フロップ - コミュニティ3枚公開] F --> G[フロップ - ベッティング] G --> H[ターン - コミュニティ4枚目公開] H --> I[ターン - ベッティング] I --> J[リバー - コミュニティ5枚目公開] J --> L[リバー - ベッティング] L --> M[ショーダウン - ハンド比較] M --> K K --> N{ゲーム続行?} N -->|はい| B N -->|いいえ| O[ゲーム終了 - 結果表示] O -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `fold` | `f` | フォールド | | `check` | `ck` | チェック | | `call` | `c` | コール | | `bet <額>` | `b <額>` | ベット | | `raise <額>` | `ra <額>` | レイズ | | `allin` | `a` | オールイン | | `muck` | `m` | マック(手札を伏せる) | | `show` | `sh` | ショー(手札を公開する) | | `rebuy` | `rb` | リバイ | | `skiprebuy` | `sr` | リバイスキップ | | `addon` | `ad` | アドオン | | `skipaddon` | `sa` | アドオンスキップ | | `bettinglimit <0-2>` | `bl <0-2>` | ベッティングリミット (0=Fixed, 1=PotLimit, 2=NoLimit) | | `tournament <0-1>` | `tm <0-1>` | トーナメントモード切替 | | `smallblind <額>` | `sb <額>` | スモールブラインド設定 | | `bigblind <額>` | `bb <額>` | ビッグブラインド設定 | | `levelhand <数>` | `lh <数>` | ブラインドレベルアップハンド数 | | `tablesize <4\|6\|9>` | `ts <4\|6\|9>` | テーブルサイズ変更 | | `metaai <0\|1>` | `mai <0\|1>` | メタAI切替 (CPUがプレイスタイルを学習) | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `c` → コール - `ra 100` → 100にレイズ - `b 50` → 50をベット - `a` → オールイン - `ts 6` → テーブルサイズを6-maxに変更 - `bl 2` → ノーリミットに変更 ## 画面の見方 ``` ========== Omaha Hold'em (オマハホールデム) ========== ハンド #5 | Pot Limit | ポット: 120 SB: 5 / BB: 10 ---------- コミュニティ: [♠A] [♥K] [♣9] [??] [??] フェーズ: フロップ ---------- [You] (1500チップ) [D] [♠Q] [♥J] [♣10] [♦8] HUD: VPIP 45% / PFR 20% / 3Bet 8% / AF 1.5 CPU 1 (980チップ) [SB] HUD: VPIP 62% / PFR 15% / 3Bet 5% / AF 0.8 CPU 2 (1200チップ) [BB] HUD: VPIP 28% / PFR 22% / 3Bet 12% / AF 2.1 CPU 3 (820チップ) HUD: VPIP 55% / PFR 10% / 3Bet 3% / AF 0.6 ---------- アクション: f/ck/c/b <額>/ra <額>/a ========== ``` - `ハンド #N`: 現在のハンド番号 - `Pot Limit`: ベッティングリミット - `ポット: N`: 現在のポット額 - `SB/BB`: ブラインド額 - `コミュニティ`: 公開されたコミュニティカード - `[D]/[SB]/[BB]`: ディーラー/スモールブラインド/ビッグブラインド位置 - `HUD`: 各プレイヤーの統計情報 ## 遊び方のコツ - テキサスホールデムと異なり、**必ずホールカード2枚を使う**ルールに注意しましょう - ダブルスーテッド(2組のスートペア)のハンドは強力です - コネクター(連続した数字)を多く含むハンドはストレートの可能性が高まります - ナッツ(最強ハンド)を狙える状況でのみ大きくベットするのが安全です - ポットリミットのため、テキサスホールデムよりもポットコントロールが重要です # ぶたのしっぽ(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶマッチング系カードゲームです。52枚のカードを裏向きに円形に並べ、順番に1枚ずつめくって中央の場に出します。出したカードのスートが場の一番上と一致すると、場のカードを全て引き取るペナルティを受けます。円形の山札がなくなった時点で最も多くカードを持っているプレイヤーの負けです。 ## 起動方法 ```sh go run ./cmd/trumpcards pigtail go run ./cmd/trumpcards --lang en pigtail # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - カードを裏向きに円形の山札として配置 - プレイヤーが順番に山札から1枚引き、中央の場札の上に表向きで置く - 出したカードのスートが場札の一番上のカードと同じスートの場合、場札を全て引き取る(ペナルティ) - 場札が空の場合はペナルティなし - CPUの手番は自動で進行する ### ゲーム終了 - 円形の山札が全てなくなったらゲーム終了 - 手札(ペナルティで引き取ったカード)が最も多いプレイヤーの負け - 手札が最も少ないプレイヤーの勝ち ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カードを円形に配置] B --> C{プレイヤーの手番?} C -->|はい| D[プレイヤーが山札から1枚引く] C -->|いいえ| E[CPUが自動で1枚引く] D --> F{スートが場札トップと一致?} E --> F F -->|はい| G[場札を全て引き取る - ペナルティ] F -->|いいえ| H[カードを場に置く] G --> I{山札が空?} H --> I I -->|はい| J[結果表示 - 最多カードが負け] I -->|いいえ| C J -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `draw` | `d` | 山札からカードを1枚引く | | `log` | `l` | 棋譜(アクションログ)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ## 画面の見方 ``` ========== Pig's Tail (ぶたのしっぽ) ========== 山札: 40枚 場札: [HEART 7] [You]: 3枚 CPU 1: 2枚 CPU 2: 4枚 CPU 3: 1枚 ---------- CPU 1 が DIAMOND 5 を出した CPU 2 が DIAMOND K を出した → スート一致! 場札を引き取る (3枚) CPU 3 が CLOVER 9 を出した 手番: あなた ========== ``` - `山札: N枚`: 円形の山札の残り枚数 - `場札: [SUIT VALUE]`: 中央の場札のトップカード - `[You]: N枚`: プレイヤーの手札(ペナルティカード)枚数 - `CPU N: M枚`: CPUの手札枚数 - `スート一致!`: 出したカードのスートが場札トップと一致した場合 ## 遊び方のコツ - このゲームは完全な運ゲームです。戦略的な要素はありません - 場札のスートを確認し、ペナルティの結果を観察しましょう - `log` コマンドで過去のアクションを振り返ることができます # パイナップルポーカー(CUI版)遊び方 ## ゲーム概要 パイナップルポーカー(Crazy Pineapple)は、テキサスホールデムの派生ゲームです。各プレイヤーに3枚のホールカードが配られ、フロップ後のベッティングラウンド終了後に1枚をディスカードします。残り2枚とコミュニティカード5枚から最強の5枚で役を作ります。テーブルサイズは4-max(デフォルト)、6-max、9-maxから選択可能です。 ## 起動方法 ```sh go run ./cmd/trumpcards pineapple go run ./cmd/trumpcards --lang en pineapple # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを使用 - 各プレイヤーに3枚のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - フロップ後のベッティング終了後、各プレイヤーは1枚をディスカード(捨てる) - 残り2枚のホールカード + コミュニティカード5枚の計7枚から、最強の5枚の組み合わせで役を作る - 最も強い役を持つプレイヤーがポットを獲得する ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード3枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ディスカード**: フロップベッティング終了後、各プレイヤーが手札から1枚を捨てる 5. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 6. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 7. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 8. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 9. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | Full House | 同じ数字3枚 + 同じ数字2枚 | | Flush | 同じスート5枚 | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略 | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ブラインド投入] B --> C[ホールカード3枚配布] C --> D[プリフロップ - ベッティング] D --> E{全員アクション完了?} E -->|いいえ| D E -->|はい| F[フロップ - 3枚公開] F --> G[ベッティング] G --> H{全員アクション完了?} H -->|いいえ| G H -->|はい| I[ディスカード - 1枚捨てる] I --> J[ターン - 4枚目公開] J --> K[ベッティング] K --> L{全員アクション完了?} L -->|いいえ| K L -->|はい| M[リバー - 5枚目公開] M --> N[ベッティング] N --> O{全員アクション完了?} O -->|いいえ| N O -->|はい| P[ショーダウン] P --> Q[勝者決定・チップ配分] Q --> Q2{プレイヤーが負けた?} Q2 -->|はい| Q3[マック/ショー選択] Q3 --> R{リバイ/アドオン対象?} Q2 -->|いいえ| R{リバイ/アドオン対象?} R -->|はい| S[リバイ/アドオン選択] S -->|reset| A R -->|いいえ| T[次のハンドへ] T -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `fold` | `f` | フォールド(降りる) | | `check` | `ck` | チェック(パス) | | `call` | `c` | コール(ベット額に合わせる) | | `bet <額>` | `b <額>` | ベットする | | `raise <額>` | `ra <額>` | レイズする | | `allin` | `a` | オールイン | | `discard ` | `d ` | ディスカード(手札のインデックス0-2を指定して1枚捨てる) | | `bl [0-2]` | | ベッティングリミット変更(0=Fixed, 1=PotLimit, 2=NoLimit) | | `ts [4\|6\|9]` | `tablesize` | テーブルサイズ変更(4-max, 6-max, 9-max) | | `rebuy` | `rb` | リバイ(チップを再購入) | | `skiprebuy` | `sr` | リバイをスキップ | | `addon` | `ad` | アドオン(チップを追加購入) | | `skipaddon` | `sa` | アドオンをスキップ | | `muck` | `m` | マック(手札を伏せる) | | `show` | `sh` | ショー(手札を公開する) | | `metaai` | `mai` | メタAIの ON/OFF を切り替え | | `log` | `l` | アクションログを表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | 例: - `b 40` → 40チップをベット - `d 1` → 手札のインデックス1の カードをディスカード - `c` → コール ## 画面の見方 ``` ========== Pineapple Poker ========== ディーラー: Player 0 コミュニティ: ♠7 ♣10 ♥3 ポット: 60 ---------- [You] チップ:970 ベット:20 手札: ♠1 ♥13 ♦5 CPU 1 (TAG) チップ:980 ベット:20 CPU 2 (LAP) チップ:990 ベット:10 [フォールド] CPU 3 (GTO) チップ:960 ベット:10 ---------- ディスカードするカードを選んでください (d 0/1/2) ========== ``` - `[You]`: プレイヤーのチップ残高とベット額 - `手札`: あなたのホールカード(3枚。ディスカード後は2枚) - `CPU N (スタイル)`: CPUのチップ残高、ベット額、状態 - `コミュニティ`: 場に公開されたコミュニティカード - `ポット`: 現在のポット額 ## 遊び方のコツ - 3枚のホールカードがあるため、プリフロップではホールデムより強い手が揃いやすいです - ディスカード時は、フロップとの相性が最も悪い1枚を捨てましょう - フラッシュドロー・ストレートドローが完成しやすい1枚を残すのが基本戦略です - ディスカード後はホールデムと同じ2枚なので、ターン以降の戦略はホールデムと共通です - 相手も3枚から良い2枚を選んでいるため、平均的なハンド強度はホールデムより高くなります # ピノクル(CUI版)遊び方 ## ゲーム概要 4人2チーム(チーム0+2 vs チーム1+3)のトリックテイキング+メルドゲーム。48枚の専用デッキ(各スート9,10,J,Q,K,Aが2枚ずつ)を使用。ビッド、メルド(役の宣言)、トリックの3つのフェーズで構成され、先にポイント上限(デフォルト1500)に到達したチームが勝利。 ## 起動方法 ```sh go run ./cmd/trumpcards pinochle go run ./cmd/trumpcards --lang en pinochle # 英語モード ``` ## ルール ### デッキ 各スート(スペード、クラブ、ハート、ダイヤ)の9, 10, J, Q, K, Aが2枚ずつ、計48枚。 ### カードランク A > 10 > K > Q > J > 9(10がKより強い) ### カードポイント A=11, 10=10, K=4, Q=3, J=2, 9=0。最終トリックボーナス=10点。合計250点/ラウンド。 ### ビッドフェーズ ディーラーの左隣から順に、予想合計ポイント(メルド+トリック)をビッド。最低ビッド20、1刻み。パスするか、現在の最高ビッドより高くビッド。最高ビッダーがトランプスートを宣言。 ### メルドフェーズ 全プレイヤーが手札のメルド(役)を公開。メルドポイントはラウンド終了時に加算(ただしチームが1トリックも取れなかった場合は没収)。 ### トリックフェーズ 12トリック。リードスートに従う義務、リードスートがなければトランプを出す義務、可能なら勝てるカードを出す義務がある。 ### 得点計算 - ビッドチーム: メルド+トリックポイントの合計がビッド以上なら加算、未満ならビッド額を失う - ディフェンダーチーム: 常に得点を加算 ### CPU難易度 - Easy (0): ランダム - Normal (1): メルド評価によるビッド判断、基本戦略 - Hard (2): 積極的ビッド、高度なトリック管理 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ビッドフェーズ] B --> C{全員パス?} C -->|はい| D[ディーラー強制ビッド] C -->|いいえ| E[最高ビッダー決定] D --> F[トランプ宣言] E --> F F --> G[メルドフェーズ] G --> H[トリックプレイ x12] H --> I[ラウンド得点計算] I --> J{ポイント上限到達?} J -->|はい| K[ゲーム終了] J -->|いいえ| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `bid ` | `b ` | ビッド(20以上) | | `pass` | `pa` | パス | | `trump ` | `t ` | トランプスート宣言(1=S,2=C,3=H,4=D) | | `meld` | `m` | メルド確認して次へ | | `play ` | `p ` | カードをプレイ | | `next` | `n` | 次のトリックへ | | `nextround` | `nr` | 次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定 | | `setlimit ` | `sl ` | ポイント上限設定 | | `hint` | `h` | ヒント表示 | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ## 遊び方のコツ - トランプスートはメルドポイントが最も高いスートを選ぶと有利 - Runはメルドの中で最も高得点(150点)なので、トランプスートのA,10,K,Q,Jが揃っているか確認 - トリックでは高ポイントカード(A=11, 10=10)を確実に取ることが重要 - パートナーがトリックを取っている場合は、ポイントの高いカードを出して得点を稼ぐ # ポーカー(5カードドロー・CUI版)遊び方 ## ゲーム概要 プレイヤーと1〜3人のCPU(設定可能)がテーブルで対戦する5カードドローポーカーです。4種のプレイスタイル(Conservative / Balanced / Aggressive / Bluffer)を持つCPU AI、オプションのジョーカーワイルドカード(0〜2枚)に対応しています。2回のベッティングラウンドと1回のカード交換を経て、最も強い役を持つプレイヤーが勝利します。 ## 起動方法 ```sh go run ./cmd/cli poker ``` ## ルール ### 役の強さ(弱い順) | 順位 | 役名 | 説明 | |------|------|------| | 0 | ハイカード | 役なし | | 1 | ワンペア | 同じ数字2枚 | | 2 | ツーペア | ペアが2組 | | 3 | スリーカード | 同じ数字3枚 | | 4 | ストレート | 連続する5枚(A-2-3-4-5 や 10-J-Q-K-A を含む) | | 5 | フラッシュ | 同じスート5枚 | | 6 | フルハウス | スリーカード + ワンペア | | 7 | フォーカード | 同じ数字4枚 | | 8 | ストレートフラッシュ | 同スート連続5枚 | | 9 | ロイヤルフラッシュ | 10-J-Q-K-A の同スート | | 10 | ファイブカード | 同じ数字5枚(ジョーカー使用時のみ成立) | ### チップシステム - 初期チップ: 1000 - アンティ(参加費): 10(各ラウンド開始時に全プレイヤーから自動徴収) - 最低ベット: 10 ### マルチプレイヤー - プレイヤー1人 + CPU 1〜3人(`scc` コマンドで変更可能、デフォルト3人) - ディーラーはラウンドごとに時計回りで交代します - アクション順はディーラーの左隣から時計回りです ### ジョーカー - CUI版ではデフォルト0枚(`sjc` コマンドで0〜2枚に変更可能) - ジョーカーはワイルドカードとして任意のカードの代わりになります - ジョーカーが含まれる場合のみ、ファイブカード(同じ数字5枚)が成立します ### CPU プレイスタイル | スタイル | 特徴 | |----------|------| | Conservative | 保守的。強い手でのみベットし、ブラフ率が低い | | Balanced | バランス型。標準的なベット判断とブラフ率 | | Aggressive | 攻撃的。高額ベットとレイズを多用する | | Bluffer | ブラフ重視。弱い手でも積極的にベット・レイズする | CPU はプレイスタイルに基づいてベット額、レイズ額、フォールド判断、ブラフ頻度を自動決定します。交換フェーズでは、フラッシュドロー・ストレートドロー等を考慮した戦略的な交換を行います。 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[アンティ徴収 + 5枚配布] B --> C[第1ベッティングラウンド] C --> D{各プレイヤーが順番にアクション} D -->|CPUの番| E[CPU自動アクション] D -->|人間の番| F{プレイヤーの選択} F -->|bet / check / call / raise / allin| G[次のプレイヤーへ] F -->|fold| H[フォールドして以降不参加] E --> G H --> G G -->|全員アクション完了| I[カード交換フェーズ] G -->|残り1人| J[最後の1人が勝利] I --> K{各プレイヤーが順番に交換} K -->|CPUの番| L[CPU自動交換] K -->|人間の番| M{exchange 0 2 4 / stand} L --> N[次のプレイヤーへ] M --> N N -->|全員交換完了| O[第2ベッティングラウンド] O --> P{各プレイヤーが順番にアクション} P -->|CPUの番| Q[CPU自動アクション] P -->|人間の番| R{プレイヤーの選択} R -->|bet / check / call / raise / allin| S[次のプレイヤーへ] R -->|fold| T[フォールドして以降不参加] Q --> S T --> S S -->|全員アクション完了| U[ショーダウン - 結果表示] S -->|残り1人| J J --> U U -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始(アンティ徴収) | | `bet N` | `b N` | N チップをベット(例: `b 20`) | | `call` | `c` | 直前のベットと同額を支払う | | `raise N` | `ra N` | 直前のベットに上乗せ(例: `ra 20`) | | `check` | `ck` | チェック(未ベット時のみ) | | `fold` | `f` | フォールド(降りる) | | `allin` | `a` | オールイン(全チップを賭ける) | | `exchange [0-4...]` | `e [0-4...]` | 指定インデックスのカードを交換(例: `e 0 2 4`) | | `stand` | `s` | カード交換なしで次へ進む | | `bettinglimit [0-2]` | `bl [0-2]` | ベッティングリミット変更(0=Fixed, 1=PotLimit, 2=NoLimit) | | `setcpucount [1-3]` | `scc [1-3]` | CPU人数を変更してリセット | | `setjokercount [0-2]` | `sjc [0-2]` | ジョーカー枚数を変更してリセット | | `lowball` | `lw` | 2-7 ローボールモードの切り替え(最弱の手が勝利) | | `odds [0-4...]` | `o [0-4...]` | 交換候補のドローオッズを表示(例: `o 0 2 4`) | | `metaai` | `mai` | メタAIの ON/OFF を切り替え(CPUが人間のプレイスタイルを学習) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ## 各フェーズの詳細 ### 第1ベッティングラウンド(DEAL フェーズ) 5枚配布後、ディーラーの左隣から時計回りで各プレイヤーがアクションを行います。CPUは自動的にアクションを実行し、人間の番になるとコマンド入力を待ちます。 - 未ベット時: `bet` / `check` / `fold` / `allin` が選択可能 - ベット済み時: `call` / `raise` / `fold` / `allin` が選択可能 - 1ラウンドあたりのレイズ上限は4回です ### カード交換フェーズ(EXCHANGE フェーズ) ディーラーの左隣から時計回りで各プレイヤーがカード交換を行います。CPUはプレイスタイルに応じた戦略で自動交換し、人間の番ではインデックス(0〜4)を指定して交換します。交換しない場合は `stand` を入力します。 ### 第2ベッティングラウンド(SECOND_BET フェーズ) 交換後の手札で再度ベッティングを行います。流れは第1ラウンドと同じです。CPUは交換後の手札評価と相手の交換枚数を考慮してアクションを決定します。 ### ショーダウン(END フェーズ) フォールドしていない全プレイヤーの手札を公開し、役を比較します。最も強い役のプレイヤーがポットを獲得します。サイドポットが発生した場合は、各ポットの対象プレイヤー間で個別に精算されます。 ## 画面の見方 ``` ========== 5-Card Draw Poker ========== ディーラー: Player 0 ポット: 40 ---------- [You] チップ:980 手札: [0]♠3 [1]♥7 [2]♦2 [3]♣9 [4]♥K CPU 1 (Balanced) チップ:980 CPU 2 (Aggressive) チップ:980 ベット:10 CPU 3 (Bluffer) チップ:980 [フォールド] ---------- [CPU行動] Player 2: ベット (10) Player 3: フォールド ``` - `ディーラー`: 現在のディーラー位置(ラウンドごとに交代) - `ポット`: 場に出ているチップ合計 - `[You]`: 人間プレイヤー - `CPU N (スタイル名)`: CPU プレイヤー(プレイスタイル表示) - `チップ`: 各プレイヤーの所持チップ - `ベット`: 現ラウンドのベット額 - `[フォールド]` / `[オールイン]`: プレイヤーの状態バッジ - `[0]`〜`[4]`: カードのインデックス(交換時に使用) - CPUの手札はショーダウンまで非表示 - ショーダウン時: 全プレイヤーの手札と役名が表示されます - `[CPU行動]`: CPU が実行したアクションの記録 - `[CPU交換]`: CPU が交換したカード枚数の記録(交換フェーズ後に表示) ### 結果表示の例 ``` ========== [結果] You: Two Pair → 60チップ獲得 CPU 1: One Pair CPU 2: High Card ``` # ピラミッド(CUI版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、7段のピラミッド型に並べたカードから、合計13になるペアを見つけて除去していきます。ピラミッドのカードをすべて除去すればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards pyramid go run ./cmd/trumpcards --lang en pyramid # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 7段のピラミッドに28枚を表向きで配る(段iにはi+1枚) - 残りの24枚は山札(ストック)に裏向きで置く - 「露出カード」(下の段の左右両方のカードが除去済み)のみ選択可能 - 最下段(7段目)のカードは常に露出状態 ### カードの値 | カード | 値 | |--------|-----| | A | 1 | | 2〜10 | 数字通り | | J | 11 | | Q | 12 | | K | 13 | ### 除去ルール - 露出カード2枚の合計が13になるペアを除去できる - K(値13)は単独で除去できる - 山札からウェイストに引いたカードとピラミッドの露出カードでペアを作れる - ウェイストのトップがKの場合、単独で除去できる ### ゲームクリア - ピラミッドの28枚すべてを除去するとゲームクリア ### ゲームオーバー - これ以上除去可能な手がない場合はギブアップでゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札が空の場合に「手詰まりです」と表示します - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カードを配る] B --> C{除去可能な手がある?} C -->|はい| D{プレイヤーのアクション} D -->|draw| E[山札からウェイストにカードを引く] D -->|rm| F[カードを除去] D -->|hint| G[ヒントを表示] D -->|undo| H[直前の操作を取り消す] E --> C F --> I{ピラミッド全除去?} G --> C H --> C I -->|いいえ| C I -->|はい| J[ゲームクリア] C -->|いいえ| K[ギブアップ → ゲームオーバー] J -->|reset| A K -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `draw` | `d` | 山札からウェイストにカードを引く | | `remove ` | `rm` | ピラミッド上のペアを除去(合計13) | | `remove ` | `rm` | ピラミッド上のK(値13)を単独除去 | | `remove w ` | `rm w` | ウェイストのトップとピラミッドのカードをペアで除去 | | `remove w` | `rm w` | ウェイストのトップのKを単独除去 | | `giveup` | `g` | ギブアップしてゲームを終了 | | `hint` | `h` | 次の一手のヒントを表示 | | `undo` | `u` | 直前の操作を元に戻す | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `d` → 山札からカードを引く - `rm 6 0 6 1` → 7段目の左端と2番目のカードをペア除去 - `rm 6 3` → 7段目の4番目のカード(K)を単独除去 - `rm w 6 2` → ウェイストのトップと7段目の3番目のカードをペア除去 - `rm w` → ウェイストのトップのKを除去 - `u` → 直前の操作を元に戻す - `h` → ヒントを表示 ## 画面の見方 ``` ========== Pyramid (ピラミッド) ========== 山札: 24枚 ウェイスト: -- ---------- ピラミッド: [♠A] [♥5] [♦8] [♣3] [♠10] [♥2] [♦7] [♣6] [♠4] [♥9] [♦Q] [♣J] [♠2] [♥3] [♦K] [♣9] [♠7] [♥6] [♦4] [♣8] [♠5] [♥Q] [♦A] [♣10] [♠3] [♥7] [♦6] [♣4] ---------- 手数: 0 rm・・・カード除去 d・・・山札を引く h・・・ヒント ========== ``` - `山札: N枚`: 山札の残りカード数 - `ウェイスト: --`: ウェイストの一番上のカード(`--`は空) - 各段のカード表示: 露出カードのみ操作可能 - `手数: N`: 現在の手数 ## 遊び方のコツ - まずピラミッドの下段から除去できるペアを探しましょう - Kが見えたらすぐに単独除去しましょう - 上段のカードを露出させるために、下段のカードを優先的に除去します - 山札を引く前に、ピラミッド内で除去できるペアがないか確認しましょう - ヒントコマンドで詰まった時のアドバイスを得られます - アンドゥを活用して、異なる戦略を試しましょう # セブンカード・スタッド(CUI版)遊び方 ## ゲーム概要 セブンカード・スタッドポーカーです。コミュニティカードを使わない古典的なスタッドポーカーで、各プレイヤーに7枚のカードが配られ、最強の5枚の組み合わせで役を作ります。テーブルサイズは2人~7人(デフォルト4人 = 1人+CPU 3人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%/3Bet%/AF)が全プレイヤーに表示されます。トーナメントモードでは、指定ハンド数ごとにアンティが自動的に上昇します。リバイ(チップ再購入)とアドオン(一回限りのチップ追加購入)にも対応しています。 ## 起動方法 ```sh go run ./cmd/trumpcards sevencardstud go run ./cmd/trumpcards --lang en sevencardstud # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを使用 - コミュニティカードなし — 各プレイヤーに個別にカードが配られる - 合計7枚(裏向き3枚 + 表向き4枚)から最強の5枚の組み合わせで役を作る - ブラインドの代わりにアンティ(全員が支払う参加費)とブリングイン(最も低いドアカードのプレイヤーが支払う強制ベット)を使用 - 各ストリートでベッティング順は表向きカードの最強ハンドから開始 ### ゲームの進行 1. **アンティ**: 全プレイヤーがアンティ(デフォルト2)を支払う 2. **サードストリート**: 裏向き2枚(ホールカード)+ 表向き1枚(ドアカード)配布。最も低いドアカードのプレイヤーがブリングイン(デフォルト5)を投入。ベッティングラウンド(スモールベット) 3. **フォースストリート**: 表向き1枚追加(計4枚)。最強の表向きハンドから開始。ベッティングラウンド(スモールベット) 4. **フィフスストリート**: 表向き1枚追加(計5枚)。最強の表向きハンドから開始。ベッティングラウンド(ビッグベット) 5. **シックスストリート**: 表向き1枚追加(計6枚)。最強の表向きハンドから開始。ベッティングラウンド(ビッグベット) 6. **セブンスストリート**: 裏向き1枚追加(計7枚)。最強の表向きハンドから開始。最後のベッティングラウンド(ビッグベット) 7. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 8. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 9. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | Full House | 同じ数字3枚 + 同じ数字2枚 | | Flush | 同じスート5枚 | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略 | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[アンティ投入] B --> C[ホールカード2枚 + ドアカード1枚配布] C --> D[サードストリート - ブリングイン + ベッティング] D --> E{全員アクション完了?} E -->|いいえ| D E -->|はい| F[フォースストリート - 4枚目配布 + ベッティング] F --> G{全員アクション完了?} G -->|いいえ| F G -->|はい| H[フィフスストリート - 5枚目配布 + ベッティング] H --> I{全員アクション完了?} I -->|いいえ| H I -->|はい| J[シックスストリート - 6枚目配布 + ベッティング] J --> K{全員アクション完了?} K -->|いいえ| J K -->|はい| L[セブンスストリート - 7枚目配布 + ベッティング] L --> M{全員アクション完了?} M -->|いいえ| L M -->|はい| N[ショーダウン] N --> O[勝者決定・チップ配分] O --> P{プレイヤーが負けた?} P -->|はい| Q[マック/ショー選択] Q --> R{リバイ/アドオン対象?} P -->|いいえ| R R -->|はい| S[リバイ/アドオン選択] S -->|reset| A R -->|いいえ| T[次のハンドへ] T -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `fold` | `f` | フォールド(降りる) | | `check` | `ck` | チェック(パス) | | `call` | `c` | コール(ベット額に合わせる) | | `bet <額>` | `b <額>` | ベットする | | `raise <額>` | `ra <額>` | レイズする | | `allin` | `a` | オールイン | | `bl [0-2]` | | ベッティングリミット変更(0=Fixed, 1=PotLimit, 2=NoLimit) | | `ts [2-7]` | `tablesize` | テーブルサイズ変更(2~7人) | | `rebuy` | `rb` | リバイ(チップを再購入) | | `skiprebuy` | `sr` | リバイをスキップ | | `addon` | `ad` | アドオン(チップを追加購入) | | `skipaddon` | `sa` | アドオンをスキップ | | `muck` | `m` | マック(手札を伏せる) | | `show` | `sh` | ショー(手札を公開する) | | `metaai` | `mai` | メタAIの ON/OFF を切り替え | | `log` | `l` | アクションログを表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | 例: - `b 40` → 40チップをベット - `ra 80` → 80チップにレイズ - `c` → コール ## 画面の見方 ``` ========== Seven Card Stud ========== ディーラー: Player 0 アンティ: 2 ブリングイン: 5 SB/BB: 10/20 ポット: 19 ---------- [You] チップ:988 ベット:5 ホール: ♠A ♥K ドア: ♣10 CPU 1 (TAG) チップ:988 ベット:5 ドア: ♦7 CPU 2 (LAP) チップ:998 ドア: ♠3 [ブリングイン] CPU 3 (GTO) チップ:988 ベット:5 ドア: ♥J ---------- [CPU行動] Player 1: コール Player 3: コール ========== ``` - `[You]`: プレイヤーのチップ残高とベット額 - `ホール`: あなたの裏向きカー��(他プレイヤーからは見えない) - `ドア`: 表向きカード(全員に見える) - `CPU N (スタイル)`: CPUのチップ残高、ベット額、状態 - `[ブリングイン]`: ブリングインを投入したプレイヤー - `[フォールド]`: フォールド済み - `[オールイン]`: オールイン状態 - `ポット`: 現在のポット額 - `[CPU行動]`: CPUが行ったアクションの記録 ## 遊び方のコツ - サードストリートで強いドアカード(A/K/Q)を見せると、弱い手を持つ対戦相手をフォールドさ��やすくなります - 他プレイヤーの表向きカードをよく観察し、自分のアウツ(必要なカード)が場に出ていないか確認しましょう - フィフスストリート以降はビッグベットになるため、弱い手で深追いしないようにしましょう - ペアが見えているプレイヤーがレイズしてきたら、スリーカード(トリップス)の可能性を考慮しましょう - HUD統計を活用して、CPUのプレイスタイルの傾向を把握しましょう - トーナメントモードではアンティが上がるため、早めにチップを稼ぐ戦略が重要です # 7並べ(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。場の7を起点に、隣接する数字のカードを順番に並べていきます。手札を最初になくしたプレイヤーが1位です。 ## 起動方法 ```sh go run ./cmd/cli sevens ``` ## ルール ### 基本ルール - 52枚のトランプを使用(オプションでジョーカー0〜2枚追加) - 各スートの7が最初に場に置かれ、プレイヤーの手札から除去される - 場に出ているカードの隣接値(±1)のカードのみ出せる - カードが出せない場合はパス(デフォルト最大5回、`passes=N` で変更可能、0で無制限) - パスを使い切り、出せるカードもない場合は失格(残りの手札が場に強制配置される) - 最初に手札をなくしたプレイヤーが1位 ### オプションルール | オプション | 説明 | |------------|------| | トンネル | AとKが循環して繋がる(A↔K) | | トンネルスキップ | 通常の±1に加え、±N離れたカードからも繋げられる(2〜12、トンネル有効時は循環ラップ) | | ジョーカー | ジョーカーを場の任意の有効位置に配置できる(0〜2枚) | | CPU戦略 | CPUの思考モード。`strategy` = 戦略的(自分に有利な手を評価)、`harassment` = 嫌がらせ特化(相手の進行を妨害重視) | | パス回数 | 最大パス回数を変更(デフォルト5回、0で無制限) | | ジョーカー上がり禁止 | 最後のカードがジョーカーの場合、出せない(パスまたは失格) | | ジョーカー回収 | ジョーカーが配置された位置に本物のカードを出すと、ジョーカーが手札に戻る | | 片側ストップ | Aを置くと上側(8-K)がブロック、Kを置くと下側(A-6)がブロックされる | | ジョーカー連続禁止 | 前の手番でジョーカーを出したプレイヤーは、次の手番でジョーカーを出せない(通常カードを出すかパスするとリセット) | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[7を場に配置 + カード配布] B --> C[CPUが自動で手番を進行] C --> D{プレイヤーの手番?} D -->|はい| E{出せるカードがある?} D -->|いいえ| F[CPUが自動でプレイ] F --> D E -->|はい| G[カードを出す / パス] E -->|いいえ| H{パス残回数 > 0?} H -->|はい| I[パス] H -->|いいえ| J[失格 - 手札を場に強制配置] G -->|play N| K[カードを場に配置] G -->|play| I K --> L{手札が空?} I --> L J --> L L -->|はい| M[順位確定] L -->|いいえ| C M --> N{全員終了?} N -->|はい| O[最終結果表示] N -->|いいえ| C O -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | デフォルト設定で新しいゲームを開始 | | `reset tunnel tunnelskip=N joker=N strategy/harassment passes=N nojokerfinish jokerreclaim endstop jokerconsban` | `r tunnel tunnelskip=N joker=N strategy/harassment passes=N nojokerfinish jokerreclaim endstop jokerconsban` | オプション指定で開始(例: `r tunnel tunnelskip=3 joker=1 harassment passes=0 nojokerfinish jokerreclaim endstop jokerconsban`) | | `play N` | `p N` | インデックスNのカードを場に出す(例: `p 3`) | | `play` | `p` | パス(パス回数を1消費) | | `j cardIdx suitInt valueInt` | - | ジョーカーを配置(例: `j 0 1 6`) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### ジョーカーコマンドの詳細 `j cardIdx suitInt valueInt` の各引数: | 引数 | 説明 | |------|------| | `cardIdx` | 手札のジョーカーのインデックス | | `suitInt` | 配置先のスート(1=SPADE, 2=CLOVER, 3=HEART, 4=DIAMOND) | | `valueInt` | 配置先の数字(1〜13) | 例: `j 0 1 6` → 手札のインデックス0のジョーカーをSPADE 6の位置に配置 ## 画面の見方 ``` ========== Sevens (7並べ) ルール: [トンネル] [ジョーカー×1] [CPU戦略] [ジョーカー上がり禁止] [片側ストップ] [ジョーカー連続禁止] ========== [You]: 12枚 (パス: 0/5) [0]SPADE 2 [1]SPADE 3 [2]HEART 10 ... CPU 1: 12枚 (パス: 1/5) CPU 2: 13枚 (パス: 0/5) CPU 3: 上がり (ランク: 1位) ---------- ボード: SPADE: 5〜9 CLOVER: 7〜7 HEART: 7〜10 DIAMOND: 4〜7 ---------- ``` - `[You]: N枚 (パス: M/5)`: プレイヤーの手札枚数とパス使用回数(無制限の場合は `パス: M/∞`) - `CPU N: M枚 (パス: X/5)`: CPUの手札枚数とパス使用回数 - `上がり (ランク: N位)`: 順位確定済みプレイヤー - `失格`: パスを使い切ったプレイヤー - `(出せるカードなし)`: 出せるカードがない強制パスの場合に表示 - `ボード`: 各スートの配置状況(最小値〜最大値) - `[0]`, `[1]`...: カードのインデックス(`play` で指定) ## 遊び方のコツ - パスはデフォルト5回まで(設定で変更可能)なので、計画的に使いましょう - 相手が出せないカードを温存して、相手のパスを消費させる戦略も有効です - トンネルルールを有効にすると、AとKが循環するため戦略の幅が広がります # ショートデック / 6+ホールデム(CUI版)遊び方 ## ゲーム概要 ショートデック(6+ホールデム)は、テキサスホールデムの変種で、36枚のデッキ(各スートの2〜5を除去)を使用します。テーブルサイズは4-max(デフォルト、1人+CPU 3人)、6-max(1人+CPU 5人)、9-max(1人+CPU 8人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。GTOスタイルはボードテクスチャ分析に基づく混合戦略で意思決定します。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%)が全プレイヤーに表示され、CPUのベットサイズはポット相対(半額〜全額)で決定されます。トーナメントモードでは、指定ハンド数ごとにブラインドが自動的に上昇します。リバイ(チップ再購入)とアドオン(一回限りのチップ追加購入)にも対応しています。 ## 起動方法 ```sh go run ./cmd/cli shortdeck ``` ## ルール ### 基本ルール - **36枚**のトランプを使用(各スートの2・3・4・5を除去) - 各プレイヤーに2枚のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - ホールカード2枚 + コミュニティカード5枚の計7枚から、最強の5枚の組み合わせで役を作る - 最も強い役を持つプレイヤーがポットを獲得する ### テキサスホールデムとの違い | 項目 | テキサスホールデム | ショートデック | |------|-------------------|---------------| | デッキ枚数 | 52枚 | 36枚(2〜5除去) | | フラッシュ vs フルハウス | フルハウス > フラッシュ | **フラッシュ > フルハウス** | | 最低ストレート | A-2-3-4-5 | **A-6-7-8-9** | ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード2枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 5. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 6. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 7. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 8. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | **Flush** | **同じスート5枚(通常のホールデムより上位)** | | **Full House** | **同じ数字3枚 + 同じ数字2枚(通常のホールデムより下位)** | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略。ドライボードでは2/3ポット、ウェットボードでは3/4ポットのベットサイズ | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ブラインド投入] B --> C[ホールカード2枚配布] C --> D[プリフロップ - ベッティング] D --> E{全員アクション完了?} E -->|いいえ| D E -->|はい| F[フロップ - 3枚公開] F --> G[ベッティング] G --> H{全員アクション完了?} H -->|いいえ| G H -->|はい| I[ターン - 4枚目公開] I --> J[ベッティング] J --> K{全員アクション完了?} K -->|いいえ| J K -->|はい| L[リバー - 5枚目公開] L --> M[ベッティング] M --> N{全員アクション完了?} N -->|いいえ| M N -->|はい| O[ショーダウン] O --> P[勝者決定・チップ配分] P --> P2{プレイヤーが負けた?} P2 -->|はい| P3[マック/ショー選択] P3 --> Q{リバイ/アドオン対象?} P2 -->|いいえ| Q{リバイ/アドオン対象?} Q -->|はい| R[リバイ/アドオン選択] R -->|reset| A Q -->|いいえ| S[次のハンドへ] S -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `fold` | `f` | フォールド(降りる) | | `check` | `ck` | チェック(パス) | | `call` | `c` | コール(ベット額に合わせる) | | `bet <額>` | `b <額>` | ベットする | | `raise <額>` | `ra <額>` | レイズする | | `allin` | `a` | オールイン | | `bl [0-2]` | | ベッティングリミット変更(0=Fixed, 1=PotLimit, 2=NoLimit) | | `ts [4\|6\|9]` | `tablesize` | テーブルサイズ変更(4-max, 6-max, 9-max) | | `rebuy` | `rb` | リバイ(チップを再購入) | | `skiprebuy` | `sr` | リバイをスキップ | | `addon` | `ad` | アドオン(チップを追加購入) | | `skipaddon` | `sa` | アドオンをスキップ | | `muck` | `m` | マック(手札を伏せる) | | `show` | `sh` | ショー(手札を公開する) | | `metaai` | `mai` | メタAIの ON/OFF を切り替え(CPUが人間のプレイスタイルを学習) | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | 例: - `b 40` → 40チップをベット - `ra 80` → 80チップにレイズ - `c` → コール ## 画面の見方 ``` ========== Short Deck (6+ Hold'em) ========== ディーラー: Player 0 コミュニティ: ♠7 ♣10 ♥9 ポット: 60 ---------- [You] チップ:970 ベット:20 手札: ♠A ♥K CPU 1 (TAG) チップ:980 ベット:20 CPU 2 (LAP) チップ:990 ベット:10 [フォールド] CPU 3 (GTO) チップ:960 ベット:10 ---------- [CPU行動] Player 1: コール Player 2: フォールド Player 3: チェック ========== ``` - `[You]`: プレイヤーのチップ残高とベット額 - `手札`: あなたのホールカード - `CPU N (スタイル)`: CPUのチップ残高、ベット額、状態 - `[フォールド]`: フォールド済み - `[オールイン]`: オールイン状態 - `コミュニティ`: 場に公開されたコミュニティカード - `ポット`: 現在のポット額 - `[CPU行動]`: CPUが行ったアクションの記録 ## HUD統計 各プレイヤーにはHUD(ヘッズアップディスプレイ)統計が表示されます: - **VPIP** (Voluntarily Put $ In Pot): プリフロップで自発的にチップを入れた割合(コール/ベット/レイズ/オールイン) - **PFR** (Pre-Flop Raise): プリフロップでレイズした割合(ベット/レイズ/オールイン) ## トーナメントモード トーナメントモードを有効にすると、指定ハンド数ごとにブラインドが自動的に上昇します。 - **BlindLevelHands**: ブラインドレベルアップまでのハンド数(デフォルト10) - **BlindMultiplier**: ブラインド倍率(デフォルト200 = 2倍) 例: BlindLevelHands=10, BlindMultiplier=200 の場合、10ハンドごとにSB/BBが2倍になります。 ## リバイ/アドオン リバイとアドオンを有効にすると、チップが不足した際や特定のハンド数経過後にチップを追加購入できます。 ### リバイ - リバイ期間内(デフォルト: 最初の10ハンド)にチップが0になった場合、リバイが可能 - 最大リバイ回数(デフォルト3回)まで繰り返しリバイ可能 - リバイで得られるチップ数はデフォルト1000 - CPUは自動的にリバイを実行 ### アドオン - 指定ハンド数(デフォルト: 10ハンド目)経過後に1回だけアドオン可能 - アドオンで得られるチップ数はデフォルト1500 - CPUは自動的にアドオンを実行 ## 遊び方のコツ - ショートデックでは36枚のデッキのため、フラッシュがフルハウスより強いです(フラッシュが成立しにくいため) - 最低ストレートはA-6-7-8-9です(2〜5が存在しないため) - デッキが小さいため、ペアやセットが通常のホールデムより成立しやすいです - ポジション(ディーラーからの距離)は重要です。後ろのポジションほど有利です - 強い手(ペア、高いカード)はプリフロップでレイズして主導権を握りましょう - LAGスタイルのCPUはブラフが多いので、良い手を持っているときはコールやレイズで対抗しましょう - GTOスタイルのCPUは確率的な混合戦略を使うため、行動パターンが読みにくいです。ボードテクスチャに注意して対応しましょう - TAGスタイルのCPUがレイズしてきたら、本当に強い手を持っている可能性が高いので注意しましょう - HUD統計を活用して、CPUのプレイスタイルの傾向を把握しましょう - トーナメントモードではブラインドが上がるため、早めにチップを稼ぐ戦略が重要です - オールインは最後の手段として使いましょう。全チップを失うリスクがあります # スペード(CUI版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。スペードが常にトランプ(切り札)で、各ラウンド開始時にビッド(獲得予想トリック数)を宣言します。先に500点に到達したプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/trumpcards spades go run ./cmd/trumpcards --lang en spades # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを4人に配布(1人13枚) - **スペードが常にトランプ(切り札)** - 各ラウンド開始時にビッド(0〜13)を宣言 - ビッド0 = ニル(高リスク・高リターン) - 2♣を持つプレイヤーが最初のトリックをリード - リードスートに従う(フォロースート)。スペードはブレイクされるまでリードできない - トリックの勝者: スペード(トランプ)が出ていれば最高のスペード、なければリードスートの最高値 ### 得点 - **ビッド成功**: ビッド×10 + オーバートリック(バッグ) - **ビッド失敗**: -ビッド×10 - **ニル成功**: +100点 - **ニル失敗**: -100点 - **バッグペナルティ**: 累積10バッグごとに-100点 ### ゲーム終了 - **勝利条件**: 先に500点に到達 - **敗北条件**: -200点以下 ### CPU難易度 - **Easy** (0): ランダムにカードを出す - **Normal** (1): 基本的なトリック戦略(デフォルト) - **Hard** (2): 戦略的なプレイ(カウンティング等) ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各13枚] B --> C[ビッドフェーズ] C --> D[各プレイヤーがビッド宣言] D --> E[2♣がリード] E --> F{プレイヤーの手番?} F -->|はい| G[カードを選んで出す] F -->|いいえ| H[CPUが自動でカードを出す] G --> I[トリック完了] H --> I I --> J{13トリック終了?} J -->|いいえ| F J -->|はい| K[ラウンド終了 - 得点計算] K --> L{勝利/敗北条件を満たした?} L -->|いいえ| B L -->|はい| M[ゲーム終了 - 結果表示] M -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | ゲームリセット(設定保持) | | `bid ` | `b ` | ビッドを宣言(0=ニル, 1-13) | | `play ` | `p ` | カードをプレイ(インデックス指定) | | `next` | `n` | 次のトリックへ | | `nextround` | `nr` | ラウンドをスコアリングして次のラウンドへ | | `setdifficulty <0-2>` | `sd <0-2>` | CPU難易度設定(0=Easy, 1=Normal, 2=Hard) | | `setlimit ` | `sl ` | ポイント上限設定 | | `hint` | `h` | 推奨カード/ビッドのヒントを表示(ビッド・プレイフェーズで使用可) | | `log` | `l` | 棋譜表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | ヘルプ表示 | ### コマンド例 - `b 3` → ビッド3を宣言 - `b 0` → ニルビッドを宣言 - `p 2` → インデックス2のカードを出す - `sd 2` → CPU難易度をHardに設定 - `sl 300` → ポイント上限を300に設定 - `h` → 推奨カード/ビッドのヒントを表示 ## 画面の見方 ``` ========== Spades (スペード) ========== ラウンド 2 / トリック 5 スペードブレイク: はい ---------- [You]: 120点 (ビッド: 4, 獲得: 3, バッグ: 2) [0]SPADE 5 [1]CLOVER 8 [2]HEART K [3]DIAMOND 3 ... CPU 1: 80点 (ビッド: 3, 獲得: 2) CPU 2: 150点 (ビッド: 5, 獲得: 5) CPU 3: 60点 (ビッド: 0(ニル), 獲得: 0) ---------- 現在のトリック: CPU 1: CLOVER 10 CPU 2: CLOVER K 手番: あなた p ・・・カードを出す ========== ``` - `ラウンド N / トリック M`: 現在のラウンドとトリック番号 - `スペードブレイク`: スペードがブレイクされたかどうか - `[You]: N点 (ビッド: X, 獲得: Y, バッグ: Z)`: プレイヤーの累計得点、ビッド数、獲得トリック数、バッグ数 - `[0]SPADE 5`...: インデックス付きの手札カード - `現在のトリック`: 現在のトリックに出されたカード ## 遊び方のコツ - スペードの高いカード(A♠、K♠)を持っている場合はビッドを多めに設定しましょう - ニルビッドは手札に高いカードが少ない場合のみ狙いましょう - バッグ(オーバートリック)が累積10に近い場合は、トリックを取りすぎないよう注意しましょう - 特定のスートをボイド(手札からなくす)にして、スペードで切る戦略が有効です - 他のプレイヤーのビッドと獲得トリック数を把握し、戦略を調整しましょう - 迷った時は `h` コマンドでヒントを表示できます。ビッドフェーズでは推奨ビッド数、プレイフェーズでは推奨カードが表示されます # スピード(CUI版)遊び方 ## ゲーム概要 2人(プレイヤー1人 + CPU 1人)で遊ぶスピード系カードゲームです。中央の2つの場札に対して、ランクが±1のカードを素早く出していき、先に手札と山札をすべてなくしたプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/trumpcards speed go run ./cmd/trumpcards --lang en speed # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプを2人に26枚ずつ配布 - 各プレイヤーは手札(最大4枚)と山札を持つ - 中央に2つの場札がある - 場札のランク±1のカードを手札から出せる(K→Aのラップあり) - カードを出したら山札から手札を補充する - 先に手札と山札をすべてなくしたプレイヤーが勝利 ### スタック(行き詰まり) - 両プレイヤーが出せるカードがない場合、スタック状態になる - スタック時は `flip` コマンドで中央の場札に新しいカードをめくる - 山札がない場合はゲーム終了 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カード配布 - 各26枚] B --> C[手札4枚 + 中央場札2枚を配置] C --> D{プレイヤーの手番} D --> E[手札から場札±1のカードを出す] E --> F[山札から手札を補充] F --> G[CPU自動プレイ] G --> H{手札+山札が空?} H -->|はい| I[勝者確定] H -->|いいえ| J{出せるカードあり?} J -->|はい| D J -->|いいえ| K{両者スタック?} K -->|はい| L[flip で新しい場札をめくる] K -->|いいえ| D L --> D I --> M[ゲーム終了表示] M -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `play idx pile` | `p idx pile` | 手札のカードを場札に出す(idx=手札インデックス, pile=場札インデックス 0or1) | | `flip` | `f` | スタック時に新しい場札をめくる | | `hint` | `h` | 出せるカードのヒントを表示 | | `log` | `l` | アクションログを表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### `play` コマンドの詳細 `p idx pile` の各引数: | 引数 | 説明 | |------|------| | `idx` | 出す手札のインデックス(0から始まる) | | `pile` | 場札のインデックス(0 = 左, 1 = 右) | 例: - `p 0 0` → 手札インデックス0のカードを左の場札に出す - `p 2 1` → 手札インデックス2のカードを右の場札に出す ## 画面の見方 ``` ========== Speed (スピード) ========== CPU: 手札 4枚, 山札 18枚 ---------- 場札: [HEART 7] [SPADE 10] ---------- [You]: 手札 4枚, 山札 18枚 [0]CLOVER 6 [1]DIAMOND 8 [2]HEART 3 [3]SPADE Q 手番: あなた p ・・・カードを出す ========== ``` - `CPU: 手札 N枚, 山札 M枚`: CPUの手札枚数と山札残り枚数 - `場札: [SUIT VALUE] [SUIT VALUE]`: 中央の2つの場札(左=pile 0, 右=pile 1) - `[You]: 手札 N枚, 山札 M枚`: プレイヤーの手札枚数と山札残り枚数 - `[0]CLOVER 6`, `[1]DIAMOND 8`...: インデックス付きの手札カード ## CPU難易度 設定でCPUの強さを3段階から選べます。 | レベル | 名称 | 戦略 | |--------|------|------| | 0 | Easy | ランダムに1枚だけ出す | | 1 | Normal | 貪欲に複数枚を連続で出す | | 2 | Hard | ブロッキング(相手の手を封じる出し方)とコンボ(連鎖プレイ)を考慮して戦略的に出す | ## 遊び方のコツ - 場札の±1に合うカードを素早く見つけて出しましょう - K と A はラップするので、K の場には A を、A の場には K を出せます - スタック時は `flip` で新しい場札をめくって仕切り直しましょう - `hint` コマンドで出せるカードを確認できます # スパイダーソリティア(CUI版)遊び方 ## ゲーム概要 1人用のソリティアゲーム。2デッキ(104枚)を使い、10列のタブローで同スート降順のシーケンスを完成させ、K〜Aの13枚を8組すべて除去すれば勝利です。難易度は1/2/4スートから選択できます。 ## 起動方法 ```sh go run ./cmd/trumpcards spider go run ./cmd/trumpcards --lang en spider # 英語モード ``` ## ルール ### 基本ルール - 2デッキ104枚を使用 - 10列のタブローに配る(最初の4列は6枚、残り6列は5枚、各列の最後の1枚だけ表向き) - 残り50枚はストック(5回に分けて10枚ずつ配る) - タブロー間でカードを移動する。値が1つ大きいカードの上に置ける(スート不問) - 同スート降順の連続シーケンスのみグループ移動可能 - K〜Aの同スート13枚が揃うと自動的に除去される - 8組すべて除去でゲームクリア ### 難易度 - **1スート(初級)**: スペードのみの104枚 - **2スート(中級)**: スペードとハートの104枚 - **4スート(上級)**: 全4スートの104枚(2デッキ分) ### 得点 - 開始時: 500点 - 移動/配り: -1点 - スート完成: +100点 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[プレイ中] B --> C{操作選択} C -->|move| D[タブロー間移動] C -->|deal| E[ストックから配る] C -->|hint| F[ヒント表示] C -->|autocomplete| G[自動完成] C -->|giveup| H[ギブアップ] D --> I{スート完成?} I -->|Yes| J{8組完成?} I -->|No| B J -->|Yes| K[ゲームクリア] J -->|No| B E --> B H --> L[ゲームオーバー] ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset [1\|2\|4]` | `r` | 新しいゲームを開始(難易度指定可) | | `deal` | `d` | ストックから各列に1枚ずつ配る | | `move t t ` | `m` | タブロー間でカードを移動 | | `giveup` | `g` | ギブアップ | | `hint` | `h` | ヒントを表示 | | `autocomplete` | `ac` | 自動完成(全カード表向き時) | | `undo` | `u` | 直前の操作を取り消す | | `log` | `l` | 棋譜を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ## 画面の見方 ``` ========== Spider Solitaire (スパイダーソリティア) ========== Completed: 0/8 | Stock: 50枚 | Score: 500 ---------- 列0: [0]?? [1]?? [2]?? [3]?? [4]?? [5]♠5 列1: [0]?? [1]?? [2]?? [3]?? [4]?? [5]♠K ... ---------- 手数: 0 ``` ## 遊び方のコツ - まず1スート(初級)で慣れましょう - 裏カードを開けることを優先する - 空列を作ると移動の自由度が上がる - ストックを配る前に空列をなくすこと(空列があるとストックから配れない) - 同スートの降順シーケンスを意識して組み立てる # スリーカードポーカー(CUI版)遊び方 ## ゲーム概要 カジノの人気カードゲームです。3枚のカードでディーラーと勝負します。アンティベットとオプションのペアプラスサイドベットを組み合わせてプレイします。 ## 起動方法 ```sh go run ./cmd/trumpcards threecard go run ./cmd/trumpcards --lang en threecard # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - プレイヤーとディーラーにそれぞれ3枚ずつ配る - プレイヤーはアンティベット後、自分の手札を見てプレイまたはフォールドを決定 - ディーラーはQ-ハイ以上で「クオリファイ(資格あり)」 ### 手札ランキング(強い順) | ランク | 説明 | |--------|------| | ストレートフラッシュ | 同じスートの連続3枚 | | スリーオブアカインド | 同じランク3枚 | | ストレート | 連続する3枚(スート不問) | | フラッシュ | 同じスートの3枚(連続でない) | | ペア | 同じランク2枚 | | ハイカード | 上記以外 | ### ベットタイプと配当 **アンティ/プレイベット:** | 条件 | アンティ配当 | プレイ配当 | |------|-------------|-----------| | ディーラー不資格 | 1:1 | プッシュ(返却) | | プレイヤー勝ち | 1:1 | 1:1 | | ディーラー勝ち | 没収 | 没収 | | 引き分け | プッシュ | プッシュ | **アンティボーナス(プレイヤーの手札に対して自動支払い):** | 役 | 配当 | |----|------| | ストレートフラッシュ | 5:1 | | スリーオブアカインド | 4:1 | | ストレート | 1:1 | **ペアプラス(サイドベット、勝敗に関係なく支払い):** | 役 | 配当 | |----|------| | ストレートフラッシュ | 40:1 | | スリーオブアカインド | 30:1 | | ストレート | 6:1 | | フラッシュ | 3:1 | | ペア | 1:1 | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ベットフェーズ] B --> C[b アンティ額 ペアプラス額] C --> D[3枚ずつ配る] D --> E{プレイ or フォールド?} E -->|play| F[プレイベット = アンティ額] E -->|fold| G[アンティ・ペアプラス没収] F --> H[ディーラーの手札公開] H --> I{ディーラー資格あり?} I -->|Q-ハイ以上| J[勝敗判定] I -->|Q-ハイ未満| K[アンティ1:1, プレイ返却] J --> L[結果表示] K --> L G --> L L -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `bet [pairPlus]` | `b [pairPlus]` | アンティベット(金額と任意のペアプラス額を指定) | | `play` | `p` | プレイベットを置いてディーラーと勝負 | | `fold` | `f` | フォールド(アンティを放棄) | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `r` → ゲームリセット - `b 100` → 100チップをアンティベット(ペアプラスなし) - `b 100 50` → 100チップをアンティベット、50チップをペアプラスベット - `p` → プレイ(アンティと同額のプレイベットを置く) - `f` → フォールド - `l` → 棋譜を表示 ## 画面の見方 ``` ========== スリーカードポーカー ========== チップ: 1000 ---------- ベットフェーズ b・・・ベット l・・・棋譜 ========== > b 100 50 ========== スリーカードポーカー ========== チップ: 850 アンティ: 100 ペアプラス: 50 ---------- あなたの手札: ♠K ♥K ♦9 (ペア) ---------- アクションフェーズ p・・・プレイ f・・・フォールド ========== > p ========== スリーカードポーカー ========== チップ: 1150 ---------- あなたの手札: ♠K ♥K ♦9 (ペア) ディーラー: ♣J ♦8 ♠5 (ハイカード) ---------- ディーラー不資格! アンティ配当: 200 プレイ返却: 100 ペアプラス配当: 100 合計配当: 400 ---------- r・・・リセット l・・・棋譜 ========== ``` - `チップ: N`: 所持チップ数 - `アンティ: N`: アンティベット額 - `ペアプラス: N`: ペアプラスベット額 - `あなたの手札`: プレイヤーの3枚の手札と役名 - `ディーラー`: ディーラーの3枚の手札と役名 ## 遊び方のコツ - Q-6-4以上の手札でプレイするのが最適戦略です - ペアプラスはハウスエッジが高め(約7.3%)ですが、高配当のチャンスがあります - アンティ/プレイのハウスエッジは約3.4%で比較的低めです - アンティボーナスはプレイヤーに有利な追加配当なので、良い手はプレイしましょう # トリピークス(CUI版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、3つの重なり合うピラミッド(ピークス)に並べた28枚のカードから、ウェイストのトップカードと±1のランクのカードを除去していきます。タブローのカードをすべて除去すればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards tripeaks go run ./cmd/trumpcards --lang en tripeaks # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 3つの重なり合うピラミッド型に28枚を配る - 残りの24枚のうち1枚をウェイストに、23枚を山札(ストック)に置く - 「露出カード」(下の段のカードが除去済み)のみ選択可能 ### 除去ルール - ウェイストのトップカードと±1のランクのカードを除去できる - K-Aは繋がる(ラップアラウンド):KからAへ、AからKへ除去可能 - 例:ウェイストが5なら、4または6を除去できる ### ゲームクリア - タブローの28枚すべてを除去するとゲームクリア ### ゲームオーバー - これ以上除去可能な手がない場合はギブアップでゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札が空の場合に「手詰まりです」と表示します - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[カードを配る] B --> C{除去可能な手がある?} C -->|はい| D{プレイヤーのアクション} D -->|draw| E[山札からウェイストにカードを引く] D -->|rm| F[カードを除去] D -->|hint| G[ヒントを表示] D -->|undo| H[直前の操作を取り消す] E --> C F --> I{タブロー全除去?} G --> C H --> C I -->|いいえ| C I -->|はい| J[ゲームクリア] C -->|いいえ| K[ギブアップ → ゲームオーバー] J -->|reset| A K -->|reset| A ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `draw` | `d` | 山札からウェイストにカードを引く | | `remove ` | `rm` | タブローのカードを除去(ウェイストトップ±1) | | `giveup` | `g` | ギブアップしてゲームを終了 | | `hint` | `h` | 次の一手のヒントを表示 | | `undo` | `u` | 直前の操作を元に戻す | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `d` → 山札からカードを引く - `rm 3 0` → 4段目の左端のカードを除去 - `rm 0 1` → 1段目の2番目のカードを除去 - `u` → 直前の操作を元に戻す - `h` → ヒントを表示 ## 画面の見方 ``` ========== TriPeaks (トリピークス) ========== 山札: 23枚 ウェイスト: [♠5] ---------- タブロー: [♥A] [♦K] [♣7] [♠3] [♥8] [♦2] [♣9] [♠6] [♥Q] [♦4] [♣J] [♠10] [♥3] [♦7] [♣6] [♠2] [♥9] [♦8] [♣5] [♠K] [♥7] [♦A] [♣3] [♠Q] [♥6] [♦10] [♣4] [♠9] ---------- 手数: 0 rm・・・カード除去 d・・・山札を引く h・・・ヒント ========== ``` - `山札: N枚`: 山札の残りカード数 - `ウェイスト: [♠5]`: ウェイストの一番上のカード - 各段のカード表示: 露出カードのみ操作可能 - `手数: N`: 現在の手数 ## 遊び方のコツ - ウェイストのトップカードから連続して除去できるチェーンを狙いましょう - K-Aのラップアラウンドを活用して長いチェーンを作りましょう - 上段のカードを露出させるために、下段のカードを優先的に除去します - 山札を引く前に、タブロー内で除去できるカードがないか確認しましょう - ヒントコマンドで詰まった時のアドバイスを得られます - アンドゥを活用して、異なる戦略を試しましょう # ビデオポーカー(CUI版)遊び方 ## ゲーム概要 1人用カジノカードゲームです。52枚のトランプを使い、5枚のカードでポーカーの役を作ります。Jacks or Better(J以上のワンペアから配当)のペイテーブルに基づいて配当が支払われます。 ## 起動方法 ```sh go run ./cmd/trumpcards videopoker go run ./cmd/trumpcards --lang en videopoker # 英語モード ``` ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 1〜5コインをベットしてゲーム開始 - 5枚のカードが配られる - 残したいカード(ホールド)を選び、残りを交換 - 最終的な5枚の手札で役を判定し、配当を支払い ### 配当表(Jacks or Better) | 役 | 1コイン | 2コイン | 3コイン | 4コイン | 5コイン | |----|---------|---------|---------|---------|---------| | ロイヤルフラッシュ | 250 | 500 | 750 | 1000 | 4000 | | ストレートフラッシュ | 50 | 100 | 150 | 200 | 250 | | フォーカード | 25 | 50 | 75 | 100 | 125 | | フルハウス | 9 | 18 | 27 | 36 | 45 | | フラッシュ | 6 | 12 | 18 | 24 | 30 | | ストレート | 4 | 8 | 12 | 16 | 20 | | スリーカード | 3 | 6 | 9 | 12 | 15 | | ツーペア | 2 | 4 | 6 | 8 | 10 | | ジャックスオアベター | 1 | 2 | 3 | 4 | 5 | ### ジャックスオアベター - J、Q、K、Aのワンペア以上が配当の最低条件 - 10以下のワンペアは配当なし ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[ベットフェーズ] B --> C[b コイン数 でベット] C --> D[5枚のカードが配られる] D --> E[ドローフェーズ] E --> F[h インデックス でホールドするカードを選択] F --> G[残りのカードを交換] G --> H[役判定・配当] H -->|reset| B ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `bet ` | `b ` | ベットする(1〜5コイン) | | `hold ` | `h ` | カードをホールドして交換(インデックス0〜4) | | `log` | `l` | アクションログ(棋譜)を表示 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ### コマンド例 - `r` → ゲームリセット - `b 5` → 5コインベット - `h 0 2 4` → 0番目、2番目、4番目のカードをホールドし、残りを交換 - `h` → 全カード交換(ホールドなし) - `l` → 棋譜を表示 ## 画面の見方 ``` ========== ビデオポーカー ========== チップ: 100 ---------- ベットフェーズ b・・・ベット l・・・棋譜 ========== > b 3 ========== ビデオポーカー ========== チップ: 97 ---------- [0]♠A [1]♥5 [2]♦K [3]♣3 [4]♠J ---------- ドローフェーズ h・・・ホールド l・・・棋譜 ========== > h 0 2 4 ========== ビデオポーカー ========== チップ: 100 ---------- [0]♠A [1]♦9 [2]♦K [3]♥A [4]♠J ---------- ジャックスオアベター 配当: 3 ---------- r・・・リセット l・・・棋譜 ========== ``` - `チップ: N`: 所持チップ数 - `[N]♠A`: カードのインデックスとカード表示 - `配当: N`: 獲得配当 ## 遊び方のコツ - 5コインベットでロイヤルフラッシュの配当が大幅に増える(250→4000)ため、可能なら最大ベットがお得 - 高いペア(J以上)は必ずホールドする - ストレートやフラッシュのドロー(あと1枚で役が完成)は積極的に狙う - ローペア(10以下)でもスリーカード以上を狙えるのでホールドする価値がある # バカラ(Web版)遊び方 ## ゲーム概要 カジノの定番カードゲームです。プレイヤーとバンカーのどちらが勝つかを予想してベットします。カードの合計値の下一桁が9に近い方が勝ちです。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「バカラ」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - カードの点数: A=1, 2〜9=額面通り, 10/J/Q/K=0 - ハンドの値は各カードの点数の合計の下一桁 - プレイヤーとバンカーにそれぞれ2枚ずつ配る - 第3カードルール(サードカードルール)に従って追加カードが配られることがある ### ベットタイプと配当 | ベット先 | 条件 | 配当 | |---------|------|------| | プレイヤー | プレイヤーの勝ち | 2倍 | | バンカー | バンカーの勝ち | 1.95倍(5%コミッション) | | タイ | 引き分け | 9倍(8:1) | ### サイドベット | ベット先 | 条件 | 配当 | |---------|------|------| | プレイヤーペア | プレイヤーの最初の2枚が同じ数字 | 12倍(11:1) | | バンカーペア | バンカーの最初の2枚が同じ数字 | 12倍(11:1) | ### ナチュラル - 最初の2枚の合計が8または9の場合「ナチュラル」となり、第3カードは配られない ### 第3カードルール **プレイヤー側:** - 合計0〜5: 1枚引く - 合計6〜7: スタンド **バンカー側:**(プレイヤーが第3カードを引いた場合) - 合計0〜2: 必ず引く - 合計3: プレイヤーの第3カードが8以外なら引く - 合計4: プレイヤーの第3カードが2〜7なら引く - 合計5: プレイヤーの第3カードが4〜7なら引く - 合計6: プレイヤーの第3カードが6〜7なら引く - 合計7: スタンド ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[ベットフェーズ] B --> C[ベット額とベット先を選択] C --> D[ベットボタンをクリック] D --> E[カードを配る] E --> F{第3カードルール} F -->|条件を満たす| G[第3カードを配る] F -->|条件を満たさない| H[勝敗判定] G --> H H --> I[結果表示] I -->|リセットボタン| B ``` ## 画面の操作方法 ### ベットフェーズ 1. **ベット額**: 数値入力欄でベット額を設定(10刻み、最低10、最大は所持チップ数) 2. **ベット先**: ドロップダウンから「プレイヤー」「バンカー」「タイ」を選択 3. **サイドベット(任意)**: プレイヤーペア・バンカーペアのベット額を設定(0で無効) 4. **「ベット」ボタン**: クリックでベットを確定し、ゲーム開始 ### 結果フェーズ 1. プレイヤーとバンカーのカードが表示されます 2. 結果メッセージと配当が表示されます 3. サイドベットの結果(的中・不的中・配当)が表示されます 4. **「リセット」ボタン**: 新しいラウンドを開始 5. **「棋譜を見る」ボタン**: アクションログを表示 ### Big Road(罫線) 画面下部にBig Roadが表示されます。各ラウンドの結果(プレイヤー勝ち・バンカー勝ち・タイ)が色分けされた丸印で記録され、勝敗の流れ(連勝・トレンド)を視覚的に確認できます。「履歴クリア」ボタンで罫線をリセットできます。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `b` | ベット | ベットフェーズのみ | | `r` | リセット | 結果フェーズのみ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ チップ: 1000 │ ├────────────────────────────────────────────┤ │ プレイヤーの勝ち! │ │ │ │ プレイヤー: 値: 9 │ │ [♠9] [♥K] │ │ │ │ バンカー: 値: 7 │ │ [♣5] [♦2] │ │ │ │ 配当: 200 │ │ サイドベット: プレイヤーペア 的中 配当: 600 │ ├────────────────────────────────────────────┤ │ [リセット] [棋譜を見る] │ ├────────────────────────────────────────────┤ │ Big Road │ │ 🔵🔴🔴🟢🔵 ... │ │ [履歴クリア] │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ - バンカーベットの方が統計的にわずかに有利です(ハウスエッジ約1.06%) - プレイヤーベットのハウスエッジは約1.24% - タイベットはハウスエッジが高い(約14.4%)ので控えめに - ベット額の管理が重要です。一度に大きくベットせず、計画的にプレイしましょう # ブラックジャック(Web版)遊び方 ## ゲーム概要 ディーラーとプレイヤーが1対1で対戦するカードゲームです。手札の合計値を21に近づけ、21を超えずにディーラーより高い点数を目指します。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「BlackJack」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### カードの点数 | カード | 点数 | |--------|------| | 2〜10 | 数字どおり | | J, Q, K | 10 | | A | 11(合計が21を超える場合は1) | ### チップシステム - 初期チップ: 1000 - 最低ベット: 10(10の倍数で指定) - チップが10未満になると自動的に1000にリセットされます ### 勝敗判定 - プレイヤーの手札がディーラーより21に近ければ勝ち(賭け金と同額を獲得) - ナチュラルブラックジャック(最初の2枚で21)の場合、3:2の配当 - バスト(21超過)した場合は即負け - 引き分けの場合、賭け金はそのまま返却 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[ベットフェーズ] B -->|金額入力 + ベットボタン| C[カード配布] C --> ES{アーリーサレンダー有効?} ES -->|はい| ESF[アーリーサレンダーフェーズ] ESF -->|サレンダーボタン| O ESF -->|続行ボタン| D ES -->|いいえ| D{ディーラーの表札がA?} D -->|はい| E[インシュランスフェーズ] D -->|いいえ| F[アクションフェーズ] E -->|インシュランス / 辞退ボタン| F F --> G{ボタンを選択} G -->|ヒット| H[カードを1枚引く] H --> I{バスト?} I -->|はい| J[負け] I -->|いいえ| G G -->|スタンド| K[ディーラーのターン] G -->|ダブルダウン| L[ベット2倍 + 1枚引いてスタンド] L --> K G -->|スプリット| M[手札を分割して続行] M --> G G -->|サレンダー| O[半額返却して降りる] O --> K K --> N[結果表示] J --> N N -->|リセットボタン| B ``` ## 画面の操作方法 ### ベットフェーズ 1. 画面上部にプレイヤー・ディーラーのチップとデッキ数が表示されます 2. 数値入力欄にベット額を入力します(最低10、10の倍数) 3. **「PP:」** 入力欄でPerfect Pairsサイドベット額を指定(0で賭けない) 4. **「21+3:」** 入力欄で21+3サイドベット額を指定(0で賭けない) 5. **「デッキ数:」** ドロップダウンでシューのデッキ数を選択(1/2/4/6/8) 6. **「ヒント OFF/ON」** ボタンでベーシックストラテジーヒントを切り替え 7. **「H17/S17」** トグルでディーラーのソフト17ルールを切り替え(H17: ソフト17でヒット / S17: ソフト17でスタンド) 8. **「カウンティング OFF/ON」** トグルでカードカウンティング表示を切り替え 9. **「カウンティング方式」** セレクターでカウンティングシステムを選択(Hi-Lo / KO / Zen Count / Omega II) 10. **「DAS ON/OFF」** トグルでスプリット後のダブルダウン許可を切り替え 11. **「ペネトレーション:」** セレクターでデッキペネトレーション率を選択(50% / 75%) 12. **「CPU人数:」** セレクターでCPUプレイヤー数を選択(0〜3名) 13. **「ハンド数:」** セレクターで同時にプレイするハンド数を選択(1/2/3) 14. **「サレンダー:」** セレクターでサレンダールールを選択(レイトサレンダー / アーリーサレンダー / サレンダー禁止) 15. **「自動進行:」** セレクターで結果フェーズの自動進行タイマーを選択(OFF/3秒/5秒/10秒) 16. **「ベット」** ボタンをクリックしてゲーム開始 ### インシュランスフェーズ ディーラーの表向きカードがAの場合に表示されます。 - **「インシュランス」** ボタン: ベットの半額を支払い保険をかける - **「辞退」** ボタン: インシュランスを辞退する(ヒントON時: 基本戦略で推奨される場合にボタンがハイライト) ### アクションフェーズ | ボタン | 説明 | 表示条件 | |--------|------|----------| | **ヒット** | カードを1枚引く | 常に表示 | | **スタンド** | カードを引かずにターン終了 | 常に表示 | | **ダブルダウン** | ベットを2倍にし1枚だけ引く | 手札が2枚かつチップ >= ベット額(DAS OFF時はスプリット後のハンドでは非表示) | | **スプリット** | 同点数の手札を分割 | `canSplit` が true かつチップ >= ベット額 | | **サレンダー** | ベットの半額を返却して降りる | 手札が2枚 (`canSurrender` が true) | ヒントON時は基本戦略が推奨するボタンが白いリングでハイライトされ、「推奨: ○○」バナーが画面に表示されます。 ### 結果フェーズ - 勝敗メッセージが画面に表示されます - サレンダーしたハンドには `SURRENDER` バッジが表示されます - サイドベットの結果バナーが表示されます(勝ち: 黄色、負け: グレー) - **「リセット」** ボタン(パルスアニメーション付き)をクリックして次のラウンドへ - 自動進行が設定されている場合、カウントダウン後に自動的にリセットされます ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `h` | ヒット | アクションフェーズ | | `s` | スタンド | アクションフェーズ | | `d` | ダブルダウン | アクションフェーズ(ダブルダウン可能時のみ) | | `p` | スプリット | アクションフェーズ(スプリット可能時のみ) | | `u` | サレンダー | アクションフェーズ(サレンダー可能時のみ) | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌───────────────────────────────────────────────┐ │ Player: 950 chips デッキ: 1デッキ Dealer: 1000 chips │ │ H17/S17: S17 カウンティング: ON Hi-Lo RC=+3 TC=+1.5 │ ├───────────────────────────────────────────────┤ │ ディーラーエリア │ │ [カード画像] [裏向きカード] │ │ Score: ??? │ ├───────────────────────────────────────────────┤ │ インシュランス: 25 (表示時のみ) │ │ 推奨: ヒット (ヒントON かつ提案あり時) │ ├───────────────────────────────────────────────┤ │ CPUプレイヤーエリア (0〜3名) │ │ CPU1: Score: 18 Bet: 50 Chips: 900 │ │ [カード画像] [カード画像] │ ├───────────────────────────────────────────────┤ │ プレイヤーエリア │ │ ハンド1 (*) Score: 15 Bet: 50 │ │ [カード画像] [カード画像] │ ├───────────────────────────────────────────────┤ │ [ヒット] [スタンド] [ダブルダウン] │ │ [スプリット] [サレンダー] │ └───────────────────────────────────────────────┘ ``` - ディーラーのカードはゲーム中、2枚目が裏向き(スコア非表示) - 結果フェーズで全カードが表向きになりスコアが表示されます - 複数ハンド(スプリット後)の場合、アクティブなハンドに `(*)` マークが付きます - ステータスタグ: `[BUST]` バスト / `[DD]` ダブルダウン / `[BJ]` ナチュラルBJ / `SURRENDER` サレンダー ## 特殊ルール ### インシュランス ディーラーの表向きカードがAの場合に提示されます。ベットの半額を支払い、ディーラーがブラックジャックなら2:1の配当を受け取ります。 ### ダブルダウン 最初の2枚の状態でのみボタンが表示されます。ベットが2倍になり、カードを1枚だけ引いて自動的にスタンドします。 ### スプリット 最初の2枚が同じブラックジャック点数の場合にボタンが表示されます。手札を2つに分割し、それぞれに元のベットと同額が賭けられます。シングルハンド時は最大4ハンド、マルチハンド時は合計最大8ハンドまで分割できます。Aをスプリットした場合、各手札に1枚ずつ配られて自動スタンドとなります。 ### サレンダー アクションフェーズの最初の2枚の状態でのみ「サレンダー」ボタンが表示されます。クリックするとベットの半額が返却され、そのハンドは終了します。 ### サレンダールール設定 ベットフェーズの「サレンダー:」セレクターでサレンダールールを選択できます。 | ルール | 説明 | |--------|------| | レイトサレンダー(デフォルト) | ディーラーのBJ確認後にサレンダー可能 | | アーリーサレンダー | ディーラーのBJ確認前にサレンダー可能(アーリーサレンダーフェーズが追加) | | サレンダー禁止 | サレンダーボタンが非表示 | アーリーサレンダーが有効な場合、カード配布後にアーリーサレンダーフェーズが表示されます。このフェーズでは「サレンダー」ボタンと「続行」ボタンが表示され、ディーラーのBJ確認前にサレンダーするかどうかを選択できます。設定はセッション内で維持されます。 ### ベーシックストラテジーヒント ベットフェーズの「ヒント OFF/ON」ボタンで切り替えます。ONにすると、各アクション時にベーシックストラテジー(標準マルチデッキ、ディーラーはソフト17でスタンド)に基づく推奨アクションが表示されます。推奨ボタンは白いリングでハイライトされ、画面上部に「推奨: ○○」バナーが表示されます。ヒント設定はセッション内で維持されます。 ### デッキ数(シュー)設定 ベットフェーズの「デッキ数:」ドロップダウンから 1 / 2 / 4 / 6 / 8 デッキを選択できます。選択すると即座にサーバーに送信され、次のラウンドから新しいシューが使用されます。 ### ソフト17ルールトグル ベットフェーズの「H17/S17」トグルでディーラーのソフト17ルールを切り替えます。H17ではディーラーはソフト17(Aを11と数えて合計17)でもう1枚引きます。S17ではソフト17でスタンドします。設定はセッション内で維持されます。 ### カードカウンティング練習 ベットフェーズの「カウンティング OFF/ON」トグルでカードカウンティング表示を切り替えます。ONにすると画面上部にランニングカウント(RC)とトゥルーカウント(TC)が表示されます。カウントは配られたカードに応じてリアルタイムに更新されます。 「カウンティング方式」セレクターで使用するカウンティングシステムを選択できます(カウンティングON時のみ有効)。 | システム | タイプ | TCの表示 | |----------|--------|----------| | Hi-Lo | バランス型 | TC表示あり | | KO | アンバランス型 | TC=N/A | | Zen Count | バランス型 | TC表示あり | | Omega II | バランス型 | TC表示あり | KOシステムはアンバランス型のため、トゥルーカウント(TC)は `N/A` と表示されます。 ### DAS(スプリット後のダブルダウン) ベットフェーズの「DAS ON/OFF」トグルでスプリット後のダブルダウン(Double After Split)の許可/禁止を切り替えます。デフォルトはON(DAS許可)です。OFFにすると、スプリットで分割されたハンドではダブルダウンボタンが表示されなくなります。設定はセッション内で維持されます。 ### デッキペネトレーション設定 ベットフェーズの「ペネトレーション:」セレクターでデッキペネトレーション率を選択できます。50%または75%を選択でき、デフォルトは75%です。75%ではカードの75%が使用された時点でシューが再構築されます。50%ではカードの50%が使用された時点で再構築されます。設定はセッション内で維持されます。 ### マルチプレイヤーCPU席 ベットフェーズの「CPU人数:」セレクターで0〜3名のCPUプレイヤーを追加できます。CPUプレイヤーはベーシックストラテジーに基づいて自動的にプレイし、各CPUの手札・チップ・ベット状況が画面に表示されます。 カウンティングが有効な場合、CPUプレイヤーはカードカウンティングAIを使用します: - **ベットスプレッド**: カウント値に応じてベット額を変動させます(基本50チップ) | カウント | 倍率 | ベット額 | |----------|------|----------| | <= 1 | 1x | 50 | | 2 | 2x | 100 | | 3 | 4x | 200 | | 4 | 8x | 400 | | >= 5 | 16x | 800 | - **インシュランス判断**: カウント >= 3 のときインシュランスを取ります(CPUのインシュランスベット額がUI上に表示されます) - バランス型システム(Hi-Lo/Zen/Omega II)ではトゥルーカウント、KOではランニングカウントを使用します - ベット額は所持チップを上限とし、10の倍数に丸められます ### サイドベット ベットフェーズで「PP:」と「21+3:」の入力欄にサイドベット額を入力できます。メインベットと同時に賭け、カード配布直後に自動的に評価されます。結果はサイドベット結果バナーとして表示されます。 #### Perfect Pairs(プレイヤーの最初の2枚) | 結果 | 条件 | 配当 | |------|------|------| | Perfect Pair | 同じスート・同じ値 | 25:1 | | Colored Pair | 同じ色・同じ値 | 12:1 | | Mixed Pair | 異なる色・同じ値 | 6:1 | #### 21+3(プレイヤーの2枚+ディーラーのアップカード) | 結果 | 条件 | 配当 | |------|------|------| | Suited Trips | 同じスート・同じ値 | 100:1 | | Straight Flush | 同じスート・連続値 | 40:1 | | Three of a Kind | 同じ値・異なるスート | 30:1 | | Straight | 連続値 | 10:1 | | Flush | 同じスート | 5:1 | ### マルチハンド ベットフェーズの「ハンド数:」セレクターで1〜3ハンドを選択できます。各ハンドに同額のベットが賭けられ、カードはインターリーブ方式で配られます。 - **総コスト**: ベット額 × ハンド数 + サイドベット額 - **サイドベット**: ハンド0のみに適用 - **インシュランス**: ハンド0のベット額に基づいて1回のみ - **ナチュラルBJ**: ディーラーがBJなら即終了。全プレイヤーハンドがBJなら即終了。一部のみBJの場合はBJハンドを自動スタンドして続行 - **スプリット上限**: マルチハンド時は合計最大8ハンド(シングルハンド時は最大4ハンド) - **BJ配当**: マルチハンドで配られた初期ハンドでのBJは3:2配当。スプリットで生じたBJは1:1配当 ### 自動進行(オートアドバンス) ベットフェーズの「自動進行:」セレクターでOFF/3秒/5秒/10秒を選択できます。ONにすると、結果フェーズでカウントダウン後に自動的に次のラウンドへ進みます。リセットボタンにはパルスアニメーションが表示され、カウントダウン秒数がボタンに表示されます。 # コントラクトブリッジ(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶ2チーム制(0+2 vs 1+3)のトリックテイキング型カードゲームです。標準52枚のカード(各プレイヤー13枚)を使用し、オークション(ビッド)でコントラクトを決定した後、13トリックをプレイします。ラバーブリッジ方式のスコアリングで、先に2ゲームを獲得したチームがラバーに勝利します。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「コントラクトブリッジ」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のカードを4人に均等配布(1人13枚) - 4人を2チームに分ける(プレイヤー0+2 vs プレイヤー1+3) - オークション(ビッド)でコントラクト(レベル+スート/ノートランプ)を決定 - コントラクトを勝ち取ったプレイヤーがディクレアラー(宣言者) - ディクレアラーのパートナーがダミー(手札を公開し、ディクレアラーが操作) - リードスートに従う(フォロースート) - 切り札(トランプ)がある場合、切り札が勝つ ### オークション(ビッド) - ディーラーの左隣から時計回りにビッド - ビッドはレベル(1〜7)とスート(クラブ < ダイヤ < ハート < スペード < ノートランプ)の組み合わせ - **パス**: ビッドを見送る - **ダブル**: 相手チームの最後のビッドに対して宣言 - **リダブル**: 自チームへのダブルに対して宣言 - 3人連続パスでオークション終了 ### プレイ - ディクレアラーの左隣がオープニングリード - リード後、ダミーの手札が公開される - ディクレアラーはダミーのカードも操作する ### 得点(ラバーブリッジ) - コントラクトポイント(ライン以下)が100点以上でゲーム獲得 - 2ゲーム先取でラバー勝利(ボーナス 500/700点) - バルネラブル(1ゲーム獲得済み)でペナルティ/ボーナス増加 ### CPU難易度 - **Easy**: ランダムにビッド・プレイ - **Normal**: HCPに基づくビッド、基本的なトリック戦略(デフォルト) - **Hard**: HCP+分布点によるビッド、パートナー連携、切り札管理、ボイド戦略 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各13枚] C --> D[オークション開始] D --> E{ビッド?} E -->|ビッドボタン| F[レベル+スートを選択して宣言] E -->|パスボタン| G{3連続パス?} E -->|ダブル/リダブルボタン| F F --> G G -->|いいえ| E G -->|はい| H{コントラクト確定?} H -->|はい| I[ダミー公開・プレイ開始] H -->|4人全員パス| C I --> J{プレイヤーの手番?} J -->|はい| K[手札のカードをクリックして選択] J -->|いいえ| L[CPU/ダミーが自動でカードを出す] K --> M[出すボタンをクリック] M --> N[トリック完了] L --> N N -->|次のトリックボタン| O{13トリック終了?} O -->|いいえ| J O -->|はい| P[ラウンド終了 - 得点計算] P -->|次のラウンドボタン| Q{ラバー完了?} Q -->|いいえ| C Q -->|はい| R[ゲーム終了 - 結果表示] R -->|リセットボタン| B ``` ## 画面の操作方法 ### オークションフェーズ 1. **レベルセレクター** からビッドレベル(1〜7)を選択します 2. **スートセレクター** からスート(クラブ/ダイヤ/ハート/スペード/ノートランプ)を選択します 3. **「ビッド」** ボタンで選択したレベル+スートをビッドします 4. **「パス」** ボタンでビッドを見送ります 5. **「ダブル」** ボタンで相手のビッドをダブルします(有効な場合のみ表示) 6. **「リダブル」** ボタンでダブルに対してリダブルします(有効な場合のみ表示) ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します 2. フォロー必須のルールに従い、出せないカードは選択できません 3. **「カードを出す」** ボタンをクリックしてカードを場に出します 4. ディクレアラーの手番では、ダミーの手札も操作できます ### ヒント - **「ヒント」** ボタンをクリックすると、CPUの戦略に基づく推奨ビッドまたは推奨カードが表示されます ### トリック・ラウンドの進行 1. トリックが完了すると結果が表示されます 2. **「次のトリック」** ボタンで次のトリックに進みます 3. ラウンドが完了するとスコアが表示されます 4. **「次のラウンド」** ボタンで次のラウンドに進みます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 0=10枚目) | プレイヤーの手番 | | `Enter` | カードを出す | プレイヤーの手番 | | `Escape` | 選択クリア | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 13枚 │ │ 13枚(D) │ │ 13枚 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 2 / トリック 5 │ │ コントラクト: 3NT by CPU 1 (Dbl) │ │ チーム1: G1 B60 A50 │ │ チーム2: G0 B40 A0 [V] │ ├────────────────────────────────────────────┤ │ 現在のトリック │ │ CPU 1: ♠K │ ├────────────────────────────────────────────┤ │ ビッドエリア (オークション時) │ │ レベル: [1▼] スート: [NT▼] │ │ [パス] [ビッド] [ダブル] [リダブル] │ ├────────────────────────────────────────────┤ │ フッター: あなた │ │ [♥A][♥K][♠Q][♦10][♣7]... │ │ [リセット] [ヒント] [カードを出す] [次のトリック] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU の手札枚数を表示 - 手番の CPU は金色枠 - ディーラーは (D) マーク - ダミーの手札は公開表示 - **ゲーム情報**: コントラクト、ラウンド/トリック番号、チームスコア - G=ゲーム数、B=ライン以下、A=ライン以上 - [V]=バルネラブル - **現在のトリック**: トリックに出されたカード - **ビッドエリア**: オークション中に表示、レベル/スート選択とビッドボタン - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - フォロー必須のカードのみ選択可能 ## 遊び方のコツ - HCP(A=4, K=3, Q=2, J=1)が13点以上あればオープニングビッドを検討しましょう - パートナーとのビッドのやり取りで互いの手札の強さを伝え合います - ディクレアラーの場合、ダミーの手札と合わせて作戦を立てましょう - フォロースートできない場合、切り札で取るか、不要なカードを捨てるか判断しましょう - バルネラブル時はアンダートリックのペナルティが大きいため、無理なビッドは避けましょう - 迷った時はヒントボタンでCPUの戦略に基づく推奨を確認できます # カナスタ(Web版)遊び方 ## ゲーム概要 カナスタは2人対戦のラミー系カードゲームです。2デッキ+4枚のジョーカー(計108枚)を使用し、同ランクのカードを集めてメルド(3枚以上の組)を作り、7枚以上のメルド「カナスタ」の完成を目指します。先に目標スコア(デフォルト5000点)に到達したプレイヤーが勝利します。 ## 起動方法 ```sh go run ./cmd/trumpcards web # または go run ./cmd/server ``` ブラウザで `http://localhost:8080` を開き、ナビゲーションから「カナスタ」を選択してください。 ## ルール ### 基本ルール - **デッキ**: 標準52枚×2デッキ+ジョーカー4枚=108枚 - **配布**: 各プレイヤーに15枚 - **ワイルドカード**: 2とジョーカーはワイルド - **赤3**: ハート3・ダイヤ3は自動的に場に出される - **黒3**: 捨てるだけ(メルド不可、相手のピックアップをブロック) ### メルド - 同ランク3枚以上で構成 - ナチュラルカード最低2枚必要 - ワイルドカードは最大3枚まで - カナスタ = 7枚以上のメルド ### 初回メルド最低点 | 累積スコア | 最低点 | |-----------|--------| | マイナス | 15点 | | 0〜1499 | 50点 | | 1500〜2999 | 90点 | | 3000以上 | 120点 | ### 得点 | ボーナス | 点数 | |----------|------| | ナチュラルカナスタ | 500点 | | ミックスカナスタ | 300点 | | 上がりボーナス | 100点 | | コンシールド上がり | 200点 | | 赤3(1枚あたり) | 100点 | | 赤3(4枚全部) | 800点 | ### CPU難易度 - Easy: ランダムプレイ - Normal: 基本戦略 - Hard: 戦略的プレイ ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[ドローフェーズ] C --> D{山札から引く or 捨て札を取る} D -->|山札から引くボタン| E[メルドフェーズ] D -->|カード2枚選択 + 捨て札を取るボタン| E E --> F{メルドする or スキップ} F -->|カード3枚以上選択 + メルドボタン| G[ディスカードフェーズ] F -->|スキップボタン| G G --> H{カードを捨てる or 上がる} H -->|カード1枚選択 + 捨てるボタン| C H -->|上がるボタン| I[ラウンド終了] I -->|次のラウンドボタン| C I --> J{目標スコア到達?} J -->|Yes| K[ゲーム終了] J -->|No| C ``` ## 画面の操作方法 ### ドローフェーズ 1. 「山札から引く」ボタンをクリック 2. または、手札からナチュラルカード2枚を選択し「捨て札を取る」ボタンをクリック ### メルドフェーズ 1. 手札からメルドするカード3枚以上を選択し「メルド」ボタンをクリック 2. メルドしない場合は「スキップ」ボタンをクリック ### ディスカードフェーズ 1. 手札からカード1枚を選択し「捨てる」ボタンをクリック 2. カナスタがある場合は「上がる」ボタンで上がれます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|----------| | ← → | カード選択移動 | 人間の手番 | | Space | カード選択/解除 | 人間の手番 | | Enter | 確定 | カード選択中 | | Escape | 選択解除 | カード選択中 | ## 画面構成 ``` ┌──────────────────────────────────┐ │ ゲームタイトル / フェーズ表示 │ ├──────────────────────────────────┤ │ 設定パネル (CPU難易度/目標スコア) │ ├────────────────┬─────────────────┤ │ 捨て札エリア │ スコアテーブル │ │ メルド表示 │ CPU情報 │ │ 赤3表示 │ │ ├────────────────┴─────────────────┤ │ 手札エリア (カード選択) │ │ アクションボタン │ └──────────────────────────────────┘ ``` ## 遊び方のコツ - カナスタの完成を最優先にしましょう - ナチュラルカナスタは500点のボーナス - 捨て札の山が大きい時は積極的に取りましょう - ワイルドカードを捨てると山がフリーズします - 黒3で相手の山取りをブロックできます # クロックソリティア(Web版)遊び方 ## ゲーム概要 1人用の完全自動ソリティアカードゲームです。52枚のカードを時計の文字盤に見立てた13箇所(1〜12時の位置+中央のK置き場)に4枚ずつ伏せて配り、カードを1枚ずつ表向きにして対応する位置に移動させます。4枚目のKが表向きになる前に全カードを表向きにするとクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「クロックソリティア」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 13箇所の山に4枚ずつ伏せて配る - 位置1〜12 = 時計の1時〜12時の位置 - 位置13(中央)= Kの置き場 - ゲームは完全自動で進行(シャッフル後にプレイヤーの選択はなし) ### カードの移動ルール - 現在の山のトップカードを表向きにし、そのランクに対応する山の一番下へ移動させる - A = 位置1(1時) - 2〜10 = 位置2〜10 - J = 位置11(11時) - Q = 位置12(12時) - K = 中央(位置13) - 移動先の山が次の「現在の山」になる ### ゲームクリア - 4枚目のKが表向きになる前に全52枚が表向きになるとクリア - 画面にクリア演出が表示されます ### ゲームオーバー - 4枚目のKが表向きになった時点で、まだ伏せカードが残っていればゲームオーバー - Kは全て中央の山に集まるため、4枚目のKが出ると中央以外への移動ができなくなる ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[52枚を13山に4枚ずつ伏せて配る] C --> D{プレイヤーの操作} D -->|ステップボタン| E[カードを1枚表向きにして対応する山へ移動] D -->|自動再生ボタン| F[残りすべてを自動で処理] E --> G{全カードが表向き?} G -->|はい| H[ゲームクリア] G -->|いいえ| I{4枚目のKが表向きになった?} I -->|いいえ| D I -->|はい| J{伏せカードが残っている?} J -->|いいえ| H J -->|はい| K[ゲームオーバー] F --> H F --> K H -->|リセット| B K -->|リセット| B ``` ## 画面の操作方法 ### プレイフェーズ | 操作 | 説明 | |------|------| | ステップボタン | カードを1枚表向きにして対応する山へ移動する | | 自動再生ボタン | 残りのカードをすべて自動で処理してゲームを完了させる | | リセットボタン | 確認ダイアログ後に新しいゲームを開始 | ### ゲーム終了後 | 操作 | 説明 | |------|------| | リセットボタン | 新しいゲームを開始 | ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `s` | ステップ(カードを1枚進める) | プレイ中 | | `a` | 自動再生(残り全カードを処理) | プレイ中 | | `r` | リセット | 常時 | ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ナビゲーションバー │ │ クロックソリティア [JA] [EN] │ ├────────────────────────────────────────────┤ │ │ │ [ 12 ] │ │ [ 11 ] [ 1 ] │ │ [ 10 ] [K] [ 2 ] │ │ [ 9 ] [ 3 ] │ │ [ 8 ] [ 4 ] │ │ [ 7 ] [ 5 ] │ │ [ 6 ] │ │ │ │ 表向き: 2 / 52 手数: 2 │ │ 最後のカード: ♠8 │ ├────────────────────────────────────────────┤ │ [ステップ] [自動再生] [リセット] │ └────────────────────────────────────────────┘ ``` - **時計エリア**: 13山を時計の文字盤に配置して表示。各山は伏せカード(裏面)と表向きカードで構成される - **中央(K)**: Kカードが集まる中央の山 - **情報エリア**: 表向きカード枚数、手数、直前に動かしたカードを表示 - **操作ボタン**: ステップ・自動再生・リセット ## 遊び方のコツ - このゲームはシャッフル後の運だけで決まる純粋な確率ゲームです - クリア確率は約1/13(約7.7%)と言われています - 「自動再生」ボタンを押すと一気にゲームを完了させられます - 「ステップ」ボタンで1手ずつ確認しながら、ゲームの仕組みを学べます - 何度もリセットして挑戦してみましょう # クレイジーエイト(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。捨て札のトップカードとスートまたはランクが一致するカードを出していき、先に手札を全て出し切ったプレイヤーがラウンドの勝者です。ポイント上限(デフォルト200点)に達したプレイヤーが出たらマッチ終了です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「クレイジーエイト」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用、4人プレイ - 各プレイヤーに5枚ずつ配り、1枚を表向きにして捨て札の山を開始、残りは山札 - 捨て札の山のトップカードとスートまたはランクが一致するカードを出す - **8はワイルド**: 出すと次のスートを選べる - 出せるカードがなければ山札から1枚ドロー。出せればそのまま続行、出せなければパス - 山札が空になったら捨て札(トップを除く)をリサイクルして山札にする ### 得点 - ラウンド終了時、他プレイヤーの手札がスコアとして加算される - 8=50点、J/Q/K=10点、A=1点、その他=額面 ### ゲーム終了 - ポイント上限(デフォルト200点)に達したプレイヤーが出たらマッチ終了 ### CPU難易度 - **Easy**: ランダムにカードを出す - **Normal**: 基本的な戦略(デフォルト) - **Hard**: 戦略的なプレイ(8の温存等) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各5枚] C --> D[捨て札の山に1枚表向き] D --> E{プレイヤーの手番?} E -->|はい| F{出せるカードあり?} E -->|いいえ| G[CPUが自動でプレイ] F -->|はい| H[カードをクリックして選択] F -->|いいえ| I[ドローボタンをクリック] H --> J[カードを出すボタンをクリック] I --> K{ドローしたカードが出せる?} K -->|はい| J K -->|いいえ| L[パス] J --> M{8を出した?} M -->|はい| N[スート選択ボタンをクリック] M -->|いいえ| O{手札が0枚?} N --> O L --> E G --> E O -->|はい| P[ラウンド終了 - スコア表示] O -->|いいえ| E P -->|次のラウンドボタン| Q{ポイント上限到達?} Q -->|いいえ| C Q -->|はい| R[マッチ終了 - 結果表示] R -->|リセットボタン| B ``` ## 画面の操作方法 ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します(出せるカードのみ選択可能) 2. **「カードを出す」** ボタンをクリックしてカードを場に出します 3. 出せるカードがなければ **「ドロー」** ボタンで山札から1枚引きます ### スート選択(8を出した後) 1. 8を出すとスート選択ボタン(♠ / ♣ / ♥ / ♦)が表示されます 2. 選択したスートが次のプレイで要求されます ### ラウンドの進行 1. ラウンドが完了するとスコアが表示されます 2. **「次のラウンド」** ボタンで次のラウンドに進みます ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ ポイント上限: [200▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 0点(5枚) │ │ 0点(4枚) │ │ 0点(6枚) │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 1 | 山札: 27枚 │ │ 捨て札トップ: ♥7 │ ├────────────────────────────────────────────┤ │ フッター: あなた (0点) │ │ [♠3][♣8][♥Q][♦5][♠J] │ │ [リセット] [カードを出す] [ドロー] │ │ [♠][♣][♥][♦] (スート選択 - 8を出した時) │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「ポイント上限」: マッチ終了のポイント上限を設定 - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU の累計得点と手札枚数 - 手番の CPU は金色枠 - **ゲーム情報**: 現在のラウンド、山札残り枚数、捨て札トップ - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - 出せるカードのみ選択可能 - **スコアテーブル**: 各プレイヤーのラウンドスコアと累積スコア ## 遊び方のコツ - 8(ワイルド)は切り札として温存し、出せるカードがない時に使いましょう - 相手の手札枚数が少なくなったら、出しにくいスートに切り替えて妨害しましょう - 同じスートのカードが多い場合は、そのスートを続けて出すと有利です - 高得点カード(8=50点、J/Q/K=10点)は早めに処理してリスクを減らしましょう # クリベッジ(Web版)遊び方 ## ゲーム概要 2人対戦のカードゲームです。52枚のカードを使い、ペギング(カードを交互に出して得点)とハンドスコアリング(手札の組み合わせで得点)を繰り返し、先に121点に到達したプレイヤーが勝利します。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「クリベッジ」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 2人対戦(1人 vs CPU) - 各ラウンドでディーラーが交互に入れ替わる - 先に121点(設定変更可能)に到達したプレイヤーが勝利 ### ラウンドの流れ 1. **ディスカード**: 各プレイヤーに6枚配り、2枚ずつクリブに捨てる 2. **カット**: 山札からスターターカードを1枚公開(Jならディーラーに2点 = His Heels) 3. **ペギング**: 非ディーラーから交互にカードを出し、合計31を目指す 4. **ショー**: 手札4枚+スターターカードの組み合わせでスコア計算 ### ペギングの得点 | 条件 | 得点 | |------|------| | 合計が15になる | 2点 | | ペア(同ランク2枚) | 2点 | | ペアロイヤル(同ランク3枚) | 6点 | | ダブルペアロイヤル(同ランク4枚) | 12点 | | ラン(3枚以上の連番) | 枚数分 | | 合計が31になる | 2点 | | Go(相手が出せない) | 1点 | | ラストカード | 1点 | ### ハンドスコアリング(ショーフェーズ) 手札4枚+スターターカードの5枚で以下を計算: | 組み合わせ | 得点 | |-----------|------| | 15の組み合わせ | 各2点 | | ペア | 各2点 | | ラン(3枚以上の連番) | 枚数分 | | フラッシュ(手札4枚同スート) | 4点 | | フラッシュ(5枚同スート) | 5点 | | ノブス(スターターと同スートのJ) | 1点 | ### CPU難易度 | 難易度 | 説明 | |--------|------| | Easy | ランダムにプレイ | | Normal | 基本的な戦略でプレイ | | Hard | 最適なディスカードとペギング戦略 | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[6枚配る] C --> D[カードを2枚選択してクリブに捨てる] D --> E[スターターカード公開] E --> F[ペギング: カードをクリックして出す] F --> G{合計31 or 全員Go?} G -->|31 or Go| H{手札残りあり?} H -->|はい| F H -->|いいえ| I[ショー: スコア計算] I --> J[次へボタンで各手札・クリブのスコアを確認] J --> K{121点到達?} K -->|いいえ| L[次のラウンドボタン] L --> C K -->|はい| M[ゲーム終了] M -->|リセットボタン| B ``` ## 画面の操作方法 ### ディスカードフェーズ 1. 手札6枚から2枚をクリックして選択します(ハイライト表示) 2. 「捨てる」ボタンで選択したカードをクリブに送ります ### ペギングフェーズ 1. 手札のカードをクリックして出します(合計31以下になるカードのみ選択可能) 2. カードを出せない場合は「Go」ボタンをクリックします 3. CPUは自動でカードを出します ### ショーフェーズ 1. 「次へ」ボタンで各手札とクリブのスコア詳細を順に表示します - 非ディーラーの手札 → ディーラーの手札 → クリブ 2. スコアの内訳(15s、ペア、ラン、フラッシュ、ノブス)が表示されます ### ラウンド終了 **「次のラウンド」** ボタンで次のラウンドを開始します。 ### 設定変更 設定パネルからCPU難易度と目標点数を変更できます。 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ゲーム情報 │ │ ラウンド: 1 ディーラー: CPU │ │ あなた: 12点 CPU: 8点 │ ├────────────────────────────────────────────┤ │ スターターカード │ │ [♠J] │ ├────────────────────────────────────────────┤ │ ペギングエリア │ │ 合計: 15 [♥3] [♦7] [♠5] │ ├────────────────────────────────────────────┤ │ 手札エリア │ │ [♥3] [♦7] [♠9] [♣K] │ ├────────────────────────────────────────────┤ │ クリブ │ │ [??] [??] [??] [??] │ ├────────────────────────────────────────────┤ │ [捨てる] [Go] [次へ] [次のラウンド] │ │ [リセット] [設定] │ └────────────────────────────────────────────┘ ``` - **ゲーム情報**: ラウンド番号、ディーラー、各プレイヤーのスコア - **スターターカード**: カットされたカード(カットフェーズ以降表示) - **ペギングエリア**: ペギング中に出されたカードと合計値 - **手札エリア**: プレイヤーの手札(クリック可能なカードがハイライト表示) - **クリブ**: クリブのカード(ショーフェーズまで裏向き) - **ボタン**: - 「捨てる」: 選択した2枚をクリブに捨てる(ディスカードフェーズ) - 「Go」: Goを宣言(ペギングフェーズ) - 「次へ」: ショーフェーズの次のステップ - 「次のラウンド」: 次のラウンドを開始 - 「リセット」: 新しいゲームを開始 - 「設定」: CPU難易度・目標点数を変更 ## 遊び方のコツ - ディスカードでは、クリブがディーラーのものであることを意識しましょう - 自分がディーラーならクリブに良い組み合わせを残し、非ディーラーなら避けましょう - 15の組み合わせを作りやすいカード(5や10系カード)を手札に残すと高得点になりやすいです - ペギングでは相手に15や31を作らせないよう注意しましょう - ランを狙える連番のカードは手札に残しておくと有利です # 大富豪(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。手札を場に出していき、最初に手札をなくしたプレイヤーが「大富豪」、最後まで残ったプレイヤーが「大貧民」になります。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「Daifugo」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### カードの強さ(通常時) ``` 弱い ← 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A < 2 → 強い ``` ジョーカーは常に最強です。 ### デッキ 52枚の標準トランプ + ジョーカー2枚(合計54枚) ### 順位 | 順位 | 称号 | |------|------| | 1位 | 大富豪 | | 2位 | 富豪 | | 3位 | 平民 | | 4位 | 大貧民 | ### ローカルルール デフォルトで**有効**なルール: | ルール | 説明 | |--------|------| | 8切り | 8を出すと場が流れ、出したプレイヤーが続けて出せる | | スート縛り | 同じスートが連続で出ると、以降そのスートのカードしか出せない(モード設定可能: なし / 片縛り / 両縛り) | | 11バック | Jを出すとカードの強さが一時的に逆転する | | 階段 | 同じスートの連続3枚以上を一度に出せる | | カード交換 | 2ラウンド目以降、大富豪⇔大貧民で2枚、富豪⇔平民で1枚を交換 | | 革命 | 4枚同時出しでカードの強さが永続的に逆転(再度4枚出しで元に戻る) | デフォルトで**無効**なルール(設定パネルで切り替え可能): | ルール | 説明 | |--------|------| | 5飛び | 5を出すと次のプレイヤーの手番がスキップされる(スキップ人数は設定可能、デフォルト1) | | 7渡し | 7を出すと次のプレイヤーに任意のカード1枚を渡さなければならない | | 10捨て | 10を出すと手札から任意のカード1枚を捨てなければならない | | スペ3返し | 場がジョーカー1枚のとき、スペードの3で返せる | | 都落ち | 前ラウンドの大富豪が2位以下になった場合、最下位の順位と入れ替わる | | 9リバース | 9を出すとターンの進行方向が逆転する(再度9を出すと元に戻る) | | クーデター | 9を3枚同時に出すと革命が発生する | | 数縛り | 同じ数字が連続で出ると、以降その数字のカードしか出せない(スート縛りから独立した設定) | | 砂嵐 | ジョーカーでない3を3枚同時に出すと場が流れる(8切りと同様の効果) | | エンペラー | 場が空の状態で、4枚の連番カード(すべて異なるスート)を出すと革命が発生し場が流れる | | 階段革命 | 4枚以上の階段を出すと革命が発生する | | 階段縛り | 階段の上に階段を出すと階段縛りが発動する。場が流れてもリセットされず、ゲームリセット時のみ解除される。縛り中に場が空の場合、3枚以上の階段またはエンペラーのみ出せる | | 反則上がり | 8切り・ジョーカー・革命で上がると反則となり、最下位に降格される | | 12ボンバー | Qを出すと数字を選び、全プレイヤーからその数字のカードを除去する | | ブラインドカード交換 | カード交換時に、上位のプレイヤーが最も弱いカードではなくランダムなカードを渡す | ### CPU難易度 設定パネルのドロップダウンで3段階のCPU難易度を選択できます: | 難易度 | 説明 | |--------|------| | よわい(Easy) | 単純に出せる中で最も弱いカードを出す。8やジョーカーの温存、革命防止をしない | | ふつう(Normal) | バランスの取れたAI。8やジョーカーを温存し、革命を防止する(デフォルト) | | つよい(Hard) | 相手の手札枚数を考慮し、相手が残り少ない場合は強いカードから出す。戦略的にパスする場合もある | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンで開始] B --> C{2ラウンド目以降?} C -->|はい| D[カード交換ログ表示] C -->|いいえ| E[カード配布] D --> E E --> F[CPUが自動で手番を進行] F --> G{プレイヤーの手番?} G -->|はい| H{カードを選択して出す?} G -->|いいえ| I[CPUが自動でプレイ] I --> G H -->|選択して出すボタン| J[カードを場に出す] H -->|パスボタン| K[パス] J --> L{ゲーム終了?} K --> L L -->|いいえ| F L -->|はい| M[最終順位表示] M -->|リセットボタン| B ``` ## 画面の操作方法 ### カードの選択と出し方 1. 自分の手札からカードをクリックして選択します(黄色枠で強調) 2. 複数枚出す場合は複数枚をクリックして選択します 3. **「選択して出す」** ボタンをクリックしてカードを場に出します #### ドラッグ&ドロップ - 手札のカードを場札エリアにドラッグして出すことができます - ドラッグしたカードが選択中の場合は選択中のカード全枚を出します - ドラッグしたカードが未選択の場合はそのカード1枚のみ出します ### パス - **「パス」** ボタンをクリックしてパスします ### 7渡し(SevenPassEnabled が有効な場合) 7を出した後、**【7渡し】** バナーが表示されます: 1. 渡すカードを1枚クリックして選択します 2. **「渡す」** ボタンをクリックしてカードを次のプレイヤーに渡します - パスボタンは無効化されます(必ずカードを渡す必要があります) ### 10捨て(TenDiscardEnabled が有効な場合) 10を出した後、**【10捨て】** バナーが表示されます: 1. 捨てるカードを1枚クリックして選択します 2. **「捨てる」** ボタンをクリックしてカードを捨てます - パスボタンは無効化されます(必ずカードを捨てる必要があります) ### 設定パネル ページ上部の **「設定」** を展開すると、ルール設定を変更できます: - **ジョーカー枚数**: 0〜2枚を選択 - 各ローカルルールのチェックボックスで有効/無効を切り替え - **スート縛りモード**: なし(0) / 片縛り(1) / 両縛り(2)の3段階をドロップダウンで選択 - **5飛びスキップ数**: 5飛び有効時にスキップする人数を設定可能(1〜5) - **「リセット」** ボタンで設定を適用して新しいラウンドを開始 ### 手札のソート 手札エリア上部に3つのソートボタンが表示されます: - **「強さ順」**: カードの強さ順(デフォルト) - **「スート順」**: スート(♠ < ♣ < ♥ < ♦)で分類し、スート内は数字順 - **「数字順」**: 数字で分類し、数字内はスート順 現在のソート方法のボタンが強調表示されます。ソート設定はラウンドをまたいで維持されます。 ### その他のボタン - **「リセット」** ボタン: 現在の設定で新しいラウンドを開始 ### 操作の制限 - プレイヤーの手番以外はカードのクリックとボタンが無効化されます - プレイヤーのターンのCPUパネルには「考え中...」が表示されません ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | プレイヤーの手番 | | `Enter` | 選択カードを出す | プレイヤーの手番 | | `Escape` | 選択クリア | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 13枚 │ │ 上がり │ │ 13枚 │ │ │ │ │ │大富豪 │ │考え中... │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ 【革命中】 【スート縛り: SPADE】 │ │ 【11バック】 【階段】 │ ├────────────────────────────────────────────┤ │ 場札: [♠10] [♠J] │ │ (出したプレイヤー: CPU 1) │ ├────────────────────────────────────────────┤ │ カード交換ログ: │ │ 大貧民 → 大富豪: JOKER, 2 │ │ 大富豪 → 大貧民: 3, 4 │ ├────────────────────────────────────────────┤ │ [リセット] [パス] [選択して出す] │ ├────────────────────────────────────────────┤ │ [強さ順] [スート順] [数字順] │ │ プレイヤーの手札 │ │ [♠3] [♣5] [♥8] [♦K] [JOKER] ... │ └────────────────────────────────────────────┘ ``` - **ルールバッジ**: 「革命中」(赤)、「11バック」(黄)、「スート縛り」(青 + スート名)、「階段」(紫)、「9リバース」(オレンジ)、「数縛り」(ティール)がリアルタイムで表示 - **場札**: 現在場に出ているカードが表向きで表示 - **カード交換ログ**: 2ラウンド目以降のカード交換内容 - **CPU行動ログ / プレイヤー行動ログ**: 直近の行動が表示。CPUの行動は1件ずつアニメーションで順番に表示され(約800ms間隔)、アニメーション中はボタンが無効化される ## カード交換(2ラウンド目以降) ラウンド開始時に前ラウンドの結果に基づいてカード交換が自動で行われ、ログに表示されます: - 大貧民 → 大富豪: 最も強いカード2枚 - 大富豪 → 大貧民: 最も弱いカード2枚 - 平民 → 富豪: 最も強いカード1枚 - 富豪 → 平民: 最も弱いカード1枚 # デューシーズワイルド(Web版)遊び方 ## ゲーム概要 1人用カジノカードゲームです。52枚のトランプを使い、5枚のカードでポーカーの役を作ります。すべての2がワイルドカードとして扱われ、他の任意のカードの代わりになります。Deuces Wild専用のペイテーブルに基づいて配当が支払われます。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「デューシーズワイルド」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - すべての2(デュース)がワイルドカード - 1〜5コインをベットしてゲーム開始 - 5枚のカードが配られる - 残したいカード(ホールド)を選び、残りを交換 - 最終的な5枚の手札で役を判定し、配当を支払い ### 配当表(Deuces Wild) | 役 | 1コイン | 2コイン | 3コイン | 4コイン | 5コイン | |----|---------|---------|---------|---------|---------| | ナチュラルロイヤルフラッシュ | 250 | 500 | 750 | 1000 | 4000 | | フォーデュース | 200 | 400 | 600 | 800 | 1000 | | ワイルドロイヤルフラッシュ | 25 | 50 | 75 | 100 | 125 | | ファイブカード | 15 | 30 | 45 | 60 | 75 | | ストレートフラッシュ | 9 | 18 | 27 | 36 | 45 | | フォーカード | 5 | 10 | 15 | 20 | 25 | | フルハウス | 3 | 6 | 9 | 12 | 15 | | フラッシュ | 2 | 4 | 6 | 8 | 10 | | ストレート | 2 | 4 | 6 | 8 | 10 | | スリーカード | 1 | 2 | 3 | 4 | 5 | ### ワイルドカード - すべての2(デュース)は任意のカードとして扱える - ナチュラルロイヤルフラッシュ: ワイルドなしのロイヤルフラッシュ(最高配当) - ワイルドロイヤルフラッシュ: ワイルドを含むロイヤルフラッシュ - フォーデュース: 4枚の2が揃う特別役 - ファイブカード: ワイルドを使って同じランク5枚 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[ベットフェーズ] B --> C[コイン数を選択] C --> D[ベットボタンをクリック] D --> E[5枚のカードが表示される] E --> F[ドローフェーズ] F --> G[ホールドするカードをクリックで選択] G --> H[ドローボタンをクリック] H --> I[交換後のカードと役判定・配当を表示] I -->|リセットボタン| B ``` ## 画面の操作方法 ### ベットフェーズ 1. **コイン数選択**: コインセレクター(1〜5)でベット額を設定 2. **「ベット」ボタン**: クリックでベットを確定し、5枚のカードが配られる ### ドローフェーズ 1. **カード選択**: ホールドしたいカードをクリックして選択(選択したカードはハイライト表示) 2. **再クリック**: 選択を解除 3. **「ドロー」ボタン**: ホールドしていないカードを交換し、役を判定 ### 結果フェーズ 1. 最終的な5枚のカードと役名が表示されます 2. 配当額が表示されます 3. **「リセット」ボタン**: 新しいラウンドを開始 4. **「棋譜を見る」ボタン**: アクションログを表示 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `b` | ベット | ベットフェーズのみ | | `d` | ドロー | ドローフェーズのみ | | `r` | リセット | 結果フェーズのみ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ チップ: 100 │ ├────────────────────────────────────────────┤ │ 配当表 │ │ ナチュラルロイヤルフラッシュ 250 500 750 1000 4000 │ │ ... │ │ スリーカード 1 2 3 4 5 │ ├────────────────────────────────────────────┤ │ │ │ [♠A] [♥2] [♦K] [♣3] [♠J] │ │ HOLD HOLD HOLD │ │ │ │ スリーカード 配当: 9 │ ├────────────────────────────────────────────┤ │ [ドロー] [棋譜を見る] │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ - 5コインベットでナチュラルロイヤルフラッシュの配当が大幅に増える(250→4000)ため、可能なら最大ベットがお得 - 2(デュース)は常にホールドする - デュースを持っている場合、より高い役を狙いやすい - デュースなしの場合はJacks or Betterより最低配当条件が厳しい(スリーカード以上が必要) # ダウト(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。カードを場に出す際に宣言する値が本当かどうかを見極めながら、手札を最初になくしたプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ダウト」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを4人に配布(1人13枚) - 順番にカードを1枚以上場に出し、出した枚数と宣言する値を伝える - 宣言する値は本当でも嘘でも構わない(ブラフ可) - 他のプレイヤーは「ダウト(嘘だ)」または「スルー(信じる)」を選択する - **ダウトの結果**: - 宣言が嘘だった場合 → カードを出したプレイヤーが場のカードをすべて引き取る - 宣言が本当だった場合 → ダウトしたプレイヤーが場のカードをすべて引き取る - 最初に手札をなくしたプレイヤーが勝者 ### ダウト判定ウィンドウ - CPU がカードを出した後、設定した秒数(デフォルト **10秒**)のカウントダウンが表示される - カウントダウン中に「ダウト!」または「スルー」ボタンをクリックして判断する - あなたがカードを出した後は CPU が即時自動判定し、「確認」ボタンで結果を確認する - ゲーム設定パネル(▶ 設定)でカウントダウン秒数を変更できる(3秒 / 5秒 / 10秒) ### CPU のメモリ・推理 AI - CPU は公開されたカード(ダウト解決時)を記憶する - 記憶したカードと自分の手札を合わせて「**物理的に不可能な宣言**」を検出すると 100% ダウトを宣言する (例: 既に4枚すべての「7」が確認済みなのにさらに「7」が宣言された場合) - 通常時は 30% の確率でランダムにダウトを宣言する - ゲーム設定パネル(▶ 設定)で CPU の記憶精度を変更できる: - **Easy**: 約 30% 保持(CPU がよく忘れる) - **Normal**: 約 70% 保持(デフォルト) - **Hard**: 100% 保持(CPU が完璧に記憶) ### 記憶の減衰 - CPU の記憶はターンが進むにつれて確率的に忘れていく(人間らしい忘却) - 忘却確率 = 減衰率 × 経過ターン数(古い記憶ほど忘れやすい) - 減衰率は記憶力レベルに連動: - **Easy**: 0.15/ターン(忘れやすい) - **Normal**: 0.05/ターン - **Hard**: 0.0(忘れない) ### ペナルティ上限(PenaltyDrawLimit) - ダウト敗者が引き取るカードの上限枚数を設定できる - 0(デフォルト)= 無制限(敗者が場のカードをすべて引き取る) - 上限を超えた分のカードはゲームから除外される(誰の手札にも入らない) - ゲーム設定パネル(▶ 設定)で「ペナルティ上限」を変更できる(無制限 / 3枚 / 5枚 / 10枚) - ダウト結果表示時に除外されたカード枚数が表示される ### 動的ブラフ確率 - CPU のブラフ確率は状況に応じて変動する(基本 40%) - 手札残り1枚: 10%(リスク回避) - テーブルに20枚以上: 基本から −15%(引き取りリスクが大きい) - テーブルに10枚以上: 基本から −10% ### CPU テル(緊張の兆候) - CPU がブラフでカードを出した際、確率的に「テル」(💧マーク)が表示される - テルが表示された CPU は嘘をついている可能性が高い(ダウトの判断材料になる) - テルの表示確率は CPU の記憶力レベル(難易度)に連動: - **Easy**: 40%(テルが出やすい → ブラフを見破りやすい) - **Normal**: 20% - **Hard**: 5%(テルがほぼ出ない → 上級者向け) - テルはブラフ時のみ発生し、正直なプレイでは表示されない ### Meta-AI 迷い時間学習 Meta-AI が有効な場合、プレイヤーがカードを出すまでにかかった時間(迷い時間)を毎ターン計測し、セッション内で統計を蓄積する。 - **計測対象**: プレイヤーの手番開始からカード確定(「出す」ボタン押下)までの経過時間(ミリ秒) - **蓄積データ**: 全プレイの迷い時間の平均・分散(正直/ブラフを区別せず統合) - **CPU への影響**: 迷い時間の平均が長い傾向が確認されると、CPU はダウト確率を引き上げる(プレイヤーが考えすぎているときにブラフを疑いやすくなる) - API の `play` コマンド送信時に `humanPlayMs` フィールドで迷い時間をミリ秒単位で渡す(0 = 未計測) - レスポンスの `metaAI.hesitationMean` フィールドで現在セッションの平均迷い時間を確認できる ### CPU 迷い時間ディレイ 設定で「CPU迷い時間ディレイ」を有効にすると、CPUがカードを出す際の表示ディレイがブラフ状態に応じて変化する。 | 状態 | ディレイ範囲 | 直感 | |------|-------------|------| | ブラフ(60%確率) | 300–500ms(速い) | 緊張して急ぐ | | ブラフ(40%確率) | 1200–1800ms(遅い) | 考えすぎて遅れる | | 正直 | 600–1000ms(中間) | 落ち着いた反応 | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各13枚] C --> D[CPUが自動で手番を進行] D --> E{プレイヤーの手番?} E -->|はい| F[手札のカードをクリックして選択] E -->|いいえ| G[CPUが自動でカードを出す] G --> H[ダウト判定フェーズ] F --> I[宣言値を入力して出すボタン] I --> H H --> J{誰がカードを出した?} J -->|CPU| K[10秒カウントダウン表示] J -->|あなた| L[CPUが即時自動判定] K -->|ダウト!ボタン| M[ダウト宣言] K -->|スルーボタン| N[スルー] K -->|タイムアウト| N L --> O[確認ボタンで結果表示] M --> P{宣言は嘘?} N --> P O --> P P -->|はい| Q[カード出しプレイヤーが引き取り] P -->|いいえ| R[ダウトしたプレイヤーが引き取り] Q --> S{手札が空?} R --> S S -->|いいえ| D S -->|はい| T[勝者確定・ゲーム終了表示] T -->|リセットボタン| B ``` ## 画面の操作方法 ### カードの出し方(プレイフェーズ) 1. 画面下部の手札エリアに自分のカードが表示されます 2. 出したいカードをクリックすると選択状態になり、カードが少し上に浮き上がります(緑枠) 3. 複数枚選択することで一度に複数枚出せます 4. カードを選択すると「宣言する値」入力欄が表示され、自動的にフォーカスされます 5. 宣言する値(1〜13)を入力します(1=A、11=J、12=Q、13=K) 6. **「出す」** ボタンをクリックしてカードを場に出します ### ダウトの判断(CPU がカードを出した後) 1. CPU がカードを出すと「ダウトしますか?」エリアが表示されます 2. カウントダウンタイマー(残り N 秒)が表示されます 3. **「ダウト!」** ボタン: ダウトを宣言する 4. **「スルー」** ボタン: ダウトせずに次のターンへ進む 5. カウントダウンが 0 になると自動的にスルーになります ### 結果の確認(あなたがカードを出した後) 1. あなたがカードを出すと「CPUがダウトを判定中...」エリアが表示されます 2. CPU がダウトした場合は CPU 名が表示されます 3. **「確認」** ボタンをクリックしてダウト判定結果を確認します ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | プレイヤーのプレイターン | | `Enter` | 選択カードを出す | プレイヤーのプレイターン | | `Escape` | 選択クリア | プレイヤーのプレイターン | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ ダウト判定時間: [10秒▼] │ │ CPU の記憶力: [Normal▼] │ │ ペナルティ上限: [無制限▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 12枚 │ │考え中... │ │ 上がり │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ テーブルエリア │ │ 場のカード: 5枚 │ │ CPU 2が3枚出しました (宣言: 7) │ ├────────────────────────────────────────────┤ │ ダウト判定フェーズ (CPU プレイ時) │ │ ダウトしますか? │ │ 残り 8 秒 │ │ ダウト宣言CPU: CPU 1 │ │ [ダウト!] [スルー] │ ├────────────────────────────────────────────┤ │ ダウト結果 │ │ ウソでした! │ │ CPU 2が5枚引き取りました │ │ 2枚がゲームから除外されました │ │ [♠3][♥J] (公開カード) │ ├────────────────────────────────────────────┤ │ フッター: あなた (13枚) │ │ [♠2][♣5][♥9][♦J]... │ │ 宣言する値: [5▲▼] (5) │ │ [リセット] [出す] │ └────────────────────────────────────────────┘ ``` - **CPU プレイヤーエリア**: 各 CPU の手札枚数を表示 - 手番の CPU は金色枠 + 「考え中...」バッジ - 上がったプレイヤーは「上がり」バッジ(緑色) - ブラフ時に「テル」が出た CPU は 💧 マークが表示される(2秒間のアニメーション後にフェードアウト) - **テーブルエリア**: 場に積まれているカードの枚数と最後のプレイ情報 - **ダウト判定フェーズ**(CPU プレイ後): - カウントダウンタイマーと「ダウト!」「スルー」ボタン - CPU がダウトを宣言している場合はその CPU 名を表示 - **ダウト判定フェーズ**(あなたプレイ後): - 「CPUがダウトを判定中...」と「確認」ボタン - **ダウト結果**: 嘘/本当の判定結果、引き取り情報、除外枚数(ペナルティ上限設定時)、公開されたカード - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - 「宣言する値」入力欄(カード選択時に表示) - 「出す」ボタン(カードを選択すると有効化) - **設定パネル(▶ 設定)**: - 「ダウト判定時間」セレクト: 3秒 / 5秒 / 10秒 から選択 - 「CPU の記憶力」セレクト: Easy / Normal / Hard から選択 - 「ペナルティ上限」セレクト: 無制限 / 3枚 / 5枚 / 10枚 から選択 - 「Meta-AI」チェックボックス: 有効にすると、CPUがセッション内のゲーム履歴(プレイヤーのブラフ率・ダウト正答率)を学習し、ブラフやダウトの戦略を適応的に調整する - 次のリセット時に設定が適用される ## 遊び方のコツ - 手持ちのカードを見て、宣言と一致するカードが多い値を宣言すると安全です - 相手がブラフを続けているようであれば積極的にダウトしましょう - 場のカードが多いときにダウトして成功すると、相手に大量のカードを引き取らせられます - カウントダウン中にダウトするかどうか迷う場合は、場に積まれたカード枚数も判断材料にしましょう - 複数枚まとめてカードを出すと手札を素早く減らせます # ユーカー(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶ2チーム制(0+2 vs 1+3)のトリックテイキング型カードゲームです。24枚のカード(各スート9〜A)を使用し、ライトバウアー(切り札のJ)とレフトバウアー(同色スートのJ)が最強の切り札です。先に10点に到達したチームが勝者です。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ユーカー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 24枚のカード(各スート9, 10, J, Q, K, A)を4人に配布(1人5枚) - 4人を2チームに分ける(プレイヤー0+2 vs プレイヤー1+3) - ディーラーが1枚めくり(ターンアップカード)、そのスートが切り札候補 - **ライトバウアー**(切り札のJ)が最強カード - **レフトバウアー**(切り札と同色スートのJ)が2番目に強い切り札 - リードスートに従う(フォロースート) - トリックの勝者: 切り札が出ていれば最高の切り札、なければリードスートの最高値 ### ビッド(切り札決定) - **ピックアップフェーズ**: ターンアップカードのスートを切り札にするか各プレイヤーが判断 - **コールトランプフェーズ**: 全員パスした場合、ターンアップ以外のスートを切り札に宣言 - **ゴーイングアローン**: オーダーアップ/コール時にアローンを選択可(パートナーが休み) ### 得点 - **メイカー(切り札を決めたチーム)3〜4トリック**: +1点 - **マーチ(5トリック全取り)**: +2点 - **ユーカード(ディフェンダーが勝利)**: ディフェンダー +2点 - **ゴーイングアローン マーチ**: +4点 - **ゴーイングアローン 3〜4トリック**: +1点 ### ゲーム終了 - **勝利条件**: 先に10点に到達 ### CPU難易度 - **Easy**: ランダムにカードを出す - **Normal**: 基本的なトリック戦略(デフォルト) - **Hard**: 戦略的なプレイ(切り札管理等) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各5枚] C --> D[ターンアップカード公開] D --> E[ピックアップフェーズ] E --> F{オーダーアップ?} F -->|オーダーアップボタン| G[ディーラーが拾い1枚捨てる] F -->|全員パス| H[コールトランプフェーズ] H --> I[スート選択してコールボタン] G --> J[プレイフェーズ開始] I --> J J --> K{プレイヤーの手番?} K -->|はい| L[手札のカードをクリックして選択] K -->|いいえ| M[CPUが自動でカードを出す] L --> N[出すボタンをクリック] N --> O[トリック完了] M --> O O -->|次のトリックボタン| P{5トリック終了?} P -->|いいえ| K P -->|はい| Q[ラウンド終了 - 得点計算] Q -->|次のラウンドボタン| R{10点到達?} R -->|いいえ| C R -->|はい| S[ゲーム終了 - 結果表示] S -->|リセットボタン| B ``` ## 画面の操作方法 ### ピックアップフェーズ 1. ターンアップカードが中央に表示されます 2. **「オーダーアップ」** ボタンでそのスートを切り札にします 3. **「アローン」** チェックボックスを選択してからオーダーアップするとゴーイングアローンになります 4. **「パス」** ボタンで次のプレイヤーに回します ### コールトランプフェーズ 1. スートセレクターから切り札にしたいスートを選択します(ターンアップのスート以外) 2. **「コール」** ボタンで切り札を宣言します 3. **「アローン」** チェックボックスを選択してからコールするとゴーイングアローンになります 4. **「パス」** ボタンでパスします(ディーラーはパス不可) ### ディスカードフェーズ(ディーラーのみ) 1. 手札のカードをクリックして捨てるカードを選択します 2. **「捨てる」** ボタンをクリックして確定します ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します 2. フォロー必須のルールに従い、出せないカードは選択できません 3. **「カードを出す」** ボタンをクリックしてカードを場に出します ### ヒント - プレイフェーズで **「ヒント」** ボタンをクリックすると、CPUのHard戦略に基づく推奨カードが表示されます ### トリック・ラウンドの進行 1. トリックが完了すると結果が表示されます 2. **「次のトリック」** ボタンで次のトリックに進みます 3. ラウンドが完了すると得点が表示されます 4. **「次のラウンド」** ボタンで次のラウンドに進みます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`5` | カード選択/解除(1=1枚目, ..., 5=5枚目) | プレイヤーの手番 | | `Enter` | カードを出す | プレイヤーの手番 | | `Escape` | 選択クリア | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ 目標スコア: [10▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 5枚 │ │ 5枚(D) │ │ 5枚 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 3 / トリック 2 │ │ 切り札: ♥ ターンアップ: ♥K │ │ チーム1: 6点 チーム2: 4点 │ ├────────────────────────────────────────────┤ │ 現在のトリック │ │ CPU 1: ♦10 │ ├────────────────────────────────────────────┤ │ フッター: あなた │ │ [♥A][♥J][♠Q][♦K][♣9] │ │ [リセット] [ヒント] [カードを出す] [次のトリック] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「目標スコア」: 5 / 10 / 15 / 20 から選択 - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU の手札枚数を表示 - 手番の CPU は金色枠 - ディーラーは (D) マーク - **ゲーム情報**: 現在のラウンド、トリック番号、切り札スート、チームスコア - **現在のトリック**: トリックに出されたカード - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - フォロー必須のカードのみ選択可能 ## 遊び方のコツ - ライトバウアー(切り札のJ)やレフトバウアー(同色のJ)を持っている場合はオーダーアップ/コールを積極的に検討しましょう - ゴーイングアローンは手札が非常に強い場合にのみ狙いましょう(マーチで+4点) - パートナーとの連携を意識し、パートナーがリードしたスートをフォローしましょう - 切り札が少ない場合は、特定のスートをボイド(手札からなくす)にして切り札で切る戦略が有効です - ディフェンダーの場合、メイカーをユーカードにすると+2点の大チャンスです - 迷った時はヒントボタンでCPUのHard戦略に基づく推奨を確認できます # フリーセル(Web版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、8列のタブロー、4つのフリーセル、4つの組札で構成されます。すべてのカードが表向きに配られ、組札にA→Kまでスート別に積み上げればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「フリーセル」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 8列のタブローに配る(最初の4列に7枚ずつ、残りの4列に6枚ずつ)、すべて表向き - 4つのフリーセル: 一時的にカード1枚を置ける場所 - 4つの組札(ファンデーション)はスート別にA→Kの順に積み上げる - タブローでは降順かつ交互の色で重ねる(例: 黒のKの上に赤のQ) - 空いた列にはKのみ置くことができる ### スーパームーブ 一度に移動できるカード数は以下の計算式で決まります: ``` 最大移動枚数 = (1 + 空きフリーセル数) × 2^(空きタブロー列数) ``` ### ゲームクリア - 4つの組札すべてにA→Kまで13枚ずつ積み上げるとゲームクリア ### ゲームオーバー - これ以上移動可能な手がない場合はゲームオーバー ### 手詰まり検出 - 各操作の後、ソルバー(DFS + メモ化)がボード全体を解析し、解法が存在しないと判定された場合「手詰まりです」とメッセージが表示されます - 手詰まり状態では「元に戻す」またはギブアップを選択できます - ソルバーの探索上限(100,000状態)を超えた場合は手詰まりとは判定しません ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カードを配る] C --> D{移動可能な手がある?} D -->|はい| E{プレイヤーのアクション} E -->|カードをクリック| F[カードを移動] E -->|ヒントボタン| G[ヒントを表示] E -->|オートコンプリートボタン| H[自動で組札に積み上げ] E -->|元に戻すボタン| I[直前の操作を元に戻す] F --> J{組札が完成?} G --> D H --> J I --> D J -->|いいえ| D J -->|はい| K[ゲームクリア] D -->|いいえ| L[ゲームオーバー] K -->|リセットボタン| B L -->|リセットボタン| B ``` ## 画面の操作方法 ### カードを移動する 1. 移動元のカードをクリックして選択します 2. 移動先のカード(またはエリア)をクリックして移動を確定します 3. タブロー内では降順・交互色の連続したカード列をまとめて移動できます(スーパームーブ) ### フリーセルを使う 1. タブローのカードを選択し、空いているフリーセルをクリックして退避します 2. フリーセルのカードを選択し、タブローや組札をクリックして戻します ### アンドゥ(元に戻す) **「元に戻す」** ボタンまたは `z` キーで直前の操作を取り消せます。何度でも元に戻せます。 ### ヒント・オートコンプリート 1. **「ヒント」** ボタンをクリックすると、次に可能な手を提案します 2. **「オートコンプリート」** ボタンをクリックすると、組札に安全に移動できるカードを自動で移動します ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `z` | 元に戻す(アンドゥ) | プレイ中 | | `h` | ヒント | プレイ中 | | `a` | オートコンプリート | プレイ中 | | `g` | ギブアップ | プレイ中 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ゲーム情報 │ │ 手数: 15 │ ├────────────────────────────────────────────┤ │ フリーセルエリア 組札エリア │ │ [♠K][ ][ ][ ] [♠A][♥A2][ ][ ] │ ├────────────────────────────────────────────┤ │ タブロー │ │ [0] [1] [2] [3] [4] [5] [6] [7] │ │ ♥Q ♠J ♦Q ♣Q ♥9 ♠6 ♦3 ♠5 │ │ ♣J ♥10 ♣K ♦J ♣6 ♥5 ♣5 ♦7 │ │ ♦10 ♣9 ♥J ♠Q ♦5 ♦4 ♠3 ♣A │ │ ♠9 ♦8 ♠10 ♥K ♠4 ♣3 ♥2 ♠A │ │ ♥8 ♠7 ♦9 ♦K ♥3 ♠2 ♦2 ♦A │ │ ♣7 ♥6 ♣8 ♣10 ♣2 ♥4 ♣4 ♥A │ │ ♦6 ♥7 ♠8 │ ├────────────────────────────────────────────┤ │ [元に戻す] [ヒント] [オートコンプリート] │ │ [ギブアップ] [リセット] │ └────────────────────────────────────────────┘ ``` - **フリーセルエリア**: 4つの一時退避スペース(カード1枚ずつ) - **組札エリア**: 4つのスート別の組札(A→Kの順に積み上げ) - **タブロー**: 8列のカード列(すべて表向き) - **ボタン**: - 「元に戻す」: 直前の操作を取り消す - 「ヒント」: 次の一手を提案 - 「オートコンプリート」: 自動で組札へ移動 - 「ギブアップ」: 現在のゲームを終了 - 「リセット」: 新しいゲームを開始 ## 遊び方のコツ - フリーセルは一時的な退避場所です。むやみに埋めないようにしましょう - 空いたタブロー列はスーパームーブの計算に影響するため、戦略的に活用しましょう - Aが出たらすぐに組札へ移動しましょう - 空いた列にはKのみ置くことができます - ヒントボタンで詰まった時のアドバイスを得られます - オートコンプリートは組札に安全に移動できるカードがある場合に便利です # ジンラミー(Web版)遊び方 ## ゲーム概要 2人(プレイヤー1人 + CPU 1人)で遊ぶカードゲームです。手札のカードでメルド(セットやラン)を作り、デッドウッド(メルドに含まれないカード)の合計点を減らします。ポイント上限(デフォルト100点)に先に達したプレイヤーが敗北します。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ジンラミー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用、2人プレイ - 各プレイヤーに10枚ずつ配り、1枚を表向きにして捨て札の山を開始、残りは山札(ストック) - 手番ではまず山札または捨て札の山からカードを1枚ドローする - その後、手札からカードを1枚捨てるか、ノックする ### メルドとデッドウッド - **メルド**: セット(同ランク3〜4枚)またはラン(同スート3枚以上の連番) - **デッドウッド**: メルドに含まれない手札の合計点(J/Q/K=10点、A=1点、その他=額面) ### ノック・ジン・アンダーカット - **ノック**: デッドウッド合計が10以下の時にノック可能。相手はノッカーのメルドにレイオフ(カードを付け足す)できる - **ジン**: デッドウッド0でノック。25点ボーナス。相手はレイオフ不可 - **アンダーカット**: 相手のデッドウッドがノッカー以下の場合、相手に25点ボーナス ### ゲーム終了 - ポイント上限(デフォルト100点)に達したプレイヤーが出たらマッチ終了 ### CPU難易度 - **Easy**: ランダムにカードを出す - **Normal**: 基本的なメルド戦略(デフォルト) - **Hard**: 戦略的なプレイ(相手の捨て札を分析等) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各10枚] C --> D{プレイヤーの手番?} D -->|はい| E[山札/捨て札ドローボタンをクリック] D -->|いいえ| F[CPUが自動でドロー・ディスカード] E --> G{ノック可能?} G -->|ノック| H[ノックボタンをクリック] G -->|捨てる| I[カードを選択して捨てるボタン] F --> D I --> D H --> J{ジン?} J -->|はい| K[ラウンド終了 - スコア表示] J -->|いいえ| L[レイオフ: カードを選択してレイオフボタン] L --> K K -->|次のラウンドボタン| M{ポイント上限到達?} M -->|いいえ| C M -->|はい| N[マッチ終了 - 結果表示] N -->|リセットボタン| B ``` ## 画面の操作方法 ### ドローフェーズ 1. **「山札からドロー」** ボタンでストックから1枚引く 2. **「捨て札からドロー」** ボタンで捨て札の山のトップカードを引く ### ディスカード/ノックフェーズ 1. 手札のカードをクリックして選択(1枚) 2. **「捨てる」** ボタンで選択したカードを捨てる 3. **「ノック」** ボタンでノック(デッドウッド≤10の場合のみ有効) ### レイオフフェーズ 1. 相手がノックした場合(ジンでない場合)、ノッカーのメルドにカードを付け足せる 2. 手札のカードをクリックして選択し、**「レイオフ」** ボタンで付け足す ### ラウンド終了 1. **「次のラウンド」** ボタンで次のラウンドに進む ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ ポイント上限: [100▼] │ ├────────────────────────────────────────────┤ │ CPU エリア │ │ 🂠🂠🂠🂠🂠🂠🂠🂠🂠🂠 (裏向き) │ │ スコア: 30点 │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 2 | 山札: 22枚 │ │ 捨て札トップ: ♥7 │ ├────────────────────────────────────────────┤ │ フッター: あなた (45点) │ │ [♠3][♣4][♣5][♣6][♥9][♦J]... │ │ メルド: {♣4,♣5,♣6} デッドウッド: 19点 │ │ [リセット] [山札ドロー] [捨て札ドロー] │ │ [捨てる] [ノック] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「ポイント上限」: マッチ終了のポイント上限を設定 - 次のリセット時に設定が適用される - **CPUエリア**: CPUの手札(裏向き)とスコア - **ゲーム情報**: 現在のラウンド、山札残り枚数、捨て札トップ - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - メルドとデッドウッド合計点を表示 - **スコアテーブル**: 各プレイヤーのラウンドスコアと累積スコア ## 遊び方のコツ - 序盤は捨て札からドローして、メルドを素早く作りましょう - 相手がどのカードを捨てているか注意し、相手のメルドを推測しましょう - デッドウッドの高いカード(J/Q/K=10点)は早めに処理するのが有効です - ジン(デッドウッド0)を狙えるなら、ノックを待ってボーナスを獲得しましょう - 相手のノックに備えて、レイオフできるカード(相手のメルドに付け足せるカード)を意識しておきましょう # ゴーフィッシュ(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。相手にカードのランク(数字)を要求し、同じランク4枚を揃えて「ブック」を作ります。最も多くブックを集めたプレイヤーの勝ちです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「Go Fish」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 各プレイヤーに5枚ずつ配布、残りは山札 - 自分のターンに、手札に持っているランクを指定して相手に要求する - 相手がそのランクのカードを持っていれば、全て受け取り、もう一度要求できる - 相手が持っていなければ「Go Fish」 → 山札から1枚引く - 同じランク4枚が揃うと「ブック」として場に出す - 全13ブックが完成するか、山札がなくなりどのプレイヤーも行動できなくなったらゲーム終了 - 最も多くブックを持つプレイヤーが勝ち ### CPU難易度 | 難易度 | 説明 | |--------|------| | Easy | ランダムに要求 | | Normal(デフォルト) | 手札のランクのみ要求、直近の記憶に基づいて判断 | | Hard | 全要求履歴から最適な相手とランクを推測 | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 + 初期ブックチェック] C --> D{プレイヤーの手番?} D -->|はい| E[相手を選択] E --> F[ランクを選択して要求] D -->|いいえ| G[CPU行動をアニメーション再生] G --> D F --> H{相手が持っている?} H -->|はい| I[カードを受け取る → もう一度要求可能] I --> E H -->|いいえ| J[Go Fish: 山札から1枚引く] J --> K[CPU行動をアニメーション再生] K --> D I --> L{ゲーム終了?} L -->|はい| M[結果表示] L -->|いいえ| E M -->|リセットボタン| B ``` ## 画面の操作方法 ### 要求フェーズ(2ステップ) プレイヤーの手番では、以下の2ステップで要求を行います: 1. **相手を選択**: CPUプレイヤーのエリアをクリックして要求先を選択 2. **ランクを選択**: 手札に持っているランクの一覧から要求するランクをクリック ### 要求結果 - **成功**: 相手からカードを受け取り、もう一度要求可能 - **Go Fish**: 山札から1枚引く。引いたカードが要求したランクなら、もう一度要求可能 - **ブック完成**: 同じランク4枚が揃うとブック表示エリアに追加される ### CPUの手番 CPUの行動は自動で進行し、各アクションがアニメーション再生されます。 ### 設定 - CPU難易度は設定パネルから変更できます(Easy/Normal/Hard) - Meta-AI(セッション内学習)のON/OFFを切り替えられます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 5枚 │ │ 4枚 │ │ 5枚 │ │ │ │ ブック: 0 │ │ ブック: 1 │ │ ブック: 0 │ │ │ │ [選択] │ │ │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ 山札: 32枚 │ ├────────────────────────────────────────────┤ │ ブック表示エリア │ │ [♠7♥7♦7♣7] [♠K♥K♦K♣K] │ ├────────────────────────────────────────────┤ │ [ランク選択ボタン] A 3 5 7 K │ ├────────────────────────────────────────────┤ │ [リセット] [設定] │ ├────────────────────────────────────────────┤ │ プレイヤーの手札 │ │ [♠3] [♥5] [♦7] [♣7] [♥K] │ │ ブック: 0 │ └────────────────────────────────────────────┘ ``` - **CPUプレイヤーエリア**: 各CPUの手札枚数とブック数を表示。手番時にクリックして要求先を選択 - **山札**: 残り枚数を表示 - **ブック表示エリア**: 完成したブックを表向きで表示 - **ランク選択ボタン**: 手札に含まれるランクのボタンが表示され、クリックして要求 - **プレイヤーの手札**: 手札カードを表向きで表示 ## 遊び方のコツ - 手札に2枚以上持っているランクを要求すると、ブック完成に近づきやすい - CPUの要求をアニメーションで観察して、どのランクを持っているか推理しましょう - アクションログで過去の要求履歴を確認し、誰がどのランクを持っているか推測できます - Hard難易度のCPUは全要求履歴を分析して最適な要求をするため、手強い対戦相手です # ゴルフ(Web版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、7列×5段に並べた35枚の表向きカードから、ウェイストのトップカードと±1のランクのカードを除去していきます。タブローのカードをすべて除去すればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ゴルフ」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 7列×5段に35枚をすべて表向きに配る - 残りの17枚のうち1枚をウェイストに、16枚を山札(ストック)に置く - 各列の一番下の未除去カードのみ選択可能(露出カード) ### 除去ルール - ウェイストのトップカードと±1のランクのカードを除去できる - K-Aは繋がる(ラップアラウンド):KからAへ、AからKへ除去可能 - 例:ウェイストが5なら、4または6を除去できる ### ゲームクリア - タブローの35枚すべてを除去するとゲームクリア - 画面にクリア演出が表示されます ### ゲームオーバー - これ以上除去可能な手がない場合はギブアップでゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札が空の場合に「手詰まりです」と表示します - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## 操作方法 | 操作 | 説明 | キーボード | |------|------|-----------| | 山札クリック | ウェイストにカードを引く | `d` | | 露出カードクリック | カードを除去(ウェイストトップ±1) | - | | 引くボタン | 山札からカードを引く | `d` | | 元に戻すボタン | 直前の操作を取り消す | `z` | | ヒントボタン | 次の一手のヒントを表示 | `h` | | ギブアップボタン | ゲームを終了する | `g` | | リセットボタン | 新しいゲームを開始 | - | ## 画面構成 ### メインエリア - **タブロー**: 7列のカードが縦に重なって表示されます。各列の一番下の未除去カードが露出カードとして操作可能です - **山札**: 裏向きのカードスタック。クリックでウェイストにカードを引きます - **ウェイスト**: 除去の基準となるカード。除去したカードはここに積まれます ### フッターエリア - **引く**: 山札からウェイストにカードを引く - **元に戻す**: 直前の操作を取り消す(複数回可能) - **ヒント**: 除去可能なカードまたはドローを提案 - **ギブアップ**: ゲームを終了する - **リセット**: 確認ダイアログ後に新しいゲームを開始 ## 遊び方のコツ - ウェイストのトップカードから連続して除去できるチェーンを狙いましょう - K-Aのラップアラウンドを活用して長いチェーンを作りましょう - カードが多く残っている列を優先的に除去すると効率的です - 山札を引く前に、タブロー内で除去できるカードがないか確認しましょう - ヒントボタンで詰まった時のアドバイスを得られます - 元に戻すを活用して、異なる戦略を試しましょう # ハーツ(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。ハート(各1点)とQ♠(13点)を取らないようにし、誰かが100点以上になった時点で最も点数の低いプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ハーツ」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを4人に配布(1人13枚) - 各ラウンド開始時に3枚のカードをパスする(左→右→向かい→パスなし のローテーション) - 2♣を持っているプレイヤーが最初のトリックをリードする - リードされたスートに従う(フォロー必須)。なければ任意のカードを出せる - トリックの最高ランクのカード(リードスート)を出したプレイヤーがトリックを取る - ハートはブレイクされるまでリードできない(ハートブレイク) ### 得点 - ハート(♥)各1点(計13点) - スペードのクイーン(Q♠)13点 - 1ラウンドの合計は26点 ### シュート・ザ・ムーン - 1人のプレイヤーが26点すべてを取った場合、そのプレイヤーは0点、他の3人に+26点が加算される ### オムニバス・ハーツ(オプション) - 設定パネルの「オムニバス (J♦ = -10点)」チェックボックスで有効化 - J♦を獲得すると-10点(プレイヤーにとって有利) - シュート・ザ・ムーンのしきい値は16点(26 - 10)に変更される - J♦は最初のトリックでポイントカードとして扱われる(出せない) ### ゲーム終了 - いずれかのプレイヤーが100点(デフォルト)以上に達した時点でゲーム終了 - 最も点数の低いプレイヤーが勝者 ### CPU難易度 - **Easy**: ランダムにカードを出す - **Normal**: ポイントカード(ハート・Q♠)を避ける戦略(デフォルト) - **Hard**: 戦略的なプレイ(ボイド作り、カウンティング等) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各13枚] C --> D{パスフェーズ?} D -->|はい| E[3枚のカードをクリックして選択] D -->|いいえ - パスなしラウンド| F[プレイフェーズ開始] E --> G[パスボタンをクリック] G --> F F --> H[2♣がリード] H --> I{プレイヤーの手番?} I -->|はい| J[手札のカードをクリックして選択] I -->|いいえ| K[CPUが自動でカードを出す] J --> L[出すボタンをクリック] L --> M[トリック完了] K --> M M -->|次のトリックボタン| N{13トリック終了?} N -->|いいえ| I N -->|はい| O[ラウンド終了 - 得点計算] O -->|次のラウンドボタン| P{100点以上のプレイヤーあり?} P -->|いいえ| C P -->|はい| Q[ゲーム終了 - 勝者表示] Q -->|リセットボタン| B ``` ## 画面の操作方法 ### カードのパス(パスフェーズ) 1. 画面下部の手札エリアに自分のカードが表示されます 2. パスしたいカードを3枚クリックすると選択状態になり、カードが少し上に浮き上がります(緑枠) 3. 3枚選択すると **「パス」** ボタンが有効化されます 4. **「パス」** ボタンをクリックしてカードをパスします ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します 2. フォロー必須のルールに従い、出せないカードは選択できません 3. **「出す」** ボタンをクリックしてカードを場に出します ### ヒント - パスフェーズまたはプレイフェーズで **「ヒント」** ボタンをクリックすると、CPUのHard戦略に基づく推奨カードが表示されます - パスフェーズでは推奨する3枚のカード、プレイフェーズでは推奨する1枚のカードが黄色のメッセージで表示されます ### トリック・ラウンドの進行 1. トリックが完了すると結果が表示されます 2. **「次のトリック」** ボタンで次のトリックに進みます 3. ラウンドが完了すると得点が表示されます 4. **「次のラウンド」** ボタンで次のラウンドに進みます ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | パスフェーズ / プレイヤーの手番 | | `Enter` | パス実行 / カードを出す | パスフェーズ / プレイヤーの手番 | | `Escape` | 選択クリア | パスフェーズ / プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ ポイント上限: [100▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 26点 │ │ 18点 │ │ 31点 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 3 / トリック 5 │ │ パス方向: 左 ハートブレイク: はい │ ├────────────────────────────────────────────┤ │ 現在のトリック │ │ CPU 1: ♣10 CPU 2: ♣K │ ├────────────────────────────────────────────┤ │ フッター: あなた (26点) │ │ [♠2][♣5][♥9][♦J]... │ │ [リセット] [ヒント] [出す] [次のトリック] │ └────────────────────────────────────────────┘ ``` - **CPU プレイヤーエリア**: 各 CPU の累計得点と今ラウンドの得点を表示 - 手番の CPU は金色枠 - **ゲーム情報**: 現在のラウンド、トリック番号、パス方向、ハートブレイク状態 - **現在のトリック**: トリックに出されたカード - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - フォロー必須のカードのみ選択可能 - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「ポイント上限」: ゲーム終了のポイント上限を設定 - 次のリセット時に設定が適用される ## 遊び方のコツ - Q♠を持っている場合は、早めにスペードの高いカードを処理しておきましょう - ハートや高いカードを避けるために、特定のスートをボイド(手札からなくす)にする戦略が有効です - パスフェーズでは、Q♠やハートの高いカード、特定スートの残り枚数が少ないカードをパスすると良いでしょう - シュート・ザ・ムーンを狙う場合は、高いカードを多く保持し、すべてのポイントカードを確実に取れる手札構成が必要です - 他のプレイヤーがシュート・ザ・ムーンを狙っている兆候があれば、ポイントカードを1枚でも取って阻止しましょう - 迷った時はヒントボタンでCPUのHard戦略に基づく推奨カードを確認できます # テキサスホールデム(Web版)遊び方 ## ゲーム概要 テキサスホールデムポーカーです。テーブルサイズは4-max(デフォルト、1人+CPU 3人)、6-max(1人+CPU 5人)、9-max(1人+CPU 8人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。GTOスタイルはボードテクスチャ分析に基づく混合戦略で意思決定します。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%)が全プレイヤーに表示され、CPUのベットサイズはポット相対(半額~全額)で決定されます。トーナメントモードでは、指定ハンド数ごとにブラインドが自動的に上昇します。リバイ(チップ再購入)とアドオン(一回限りのチップ追加購入)にも対応しています。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「テキサスホールデム」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用 - 各プレイヤーに2枚のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - ホールカード2枚 + コミュニティカード5枚の計7枚から、最強の5枚の組み合わせで役を作る - 最も強い役を持つプレイヤーがポットを獲得する ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード2枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 5. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 6. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 7. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 8. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | Full House | 同じ数字3枚 + 同じ数字2枚 | | Flush | 同じスート5枚 | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略。ドライボードでは2/3ポット、ウェットボードでは3/4ポットのベットサイズ | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[ブラインド投入・ホールカード配布] C --> D[CPUが自動でアクション] D --> E{プレイヤーの手番?} E -->|はい| F[アクションボタンで操作] E -->|いいえ| G[CPUが自動でアクション] G --> H{フェーズ進行?} F --> H H -->|プリフロップ→フロップ| I[コミュニティカード3枚公開] H -->|フロップ→ターン| J[4枚目公開] H -->|ターン→リバー| K[5枚目公開] H -->|リバー→ショーダウン| L[全員の手札公開] I --> D J --> D K --> D L --> M[勝者決定・結果表示] M --> M2{プレイヤーが負けた?} M2 -->|はい| M3[マック/ショー選択] M3 --> N2{リバイ/アドオン対象?} M2 -->|いいえ| N2{リバイ/アドオン対象?} N2 -->|はい| O2[リバイ/アドオン選択画面] O2 -->|リセットボタン| B N2 -->|いいえ| P2[次のハンドへ] P2 -->|リセットボタン| B ``` ## 画面の操作方法 ### アクションの実行 自分の手番が来ると、画面下部にアクションボタンが表示されます。 **ベットが出ていない場合:** - **「ベット」**: ベット額を入力してベットする - **「チェック」**: パスする **ベットが出ている場合:** - **「コール」**: 現在のベット額に合わせる - **「レイズ」**: ベット額を入力してレイズする **常に選択可能:** - **「フォールド」**: 降りる - **「オールイン」**: 全チップを賭ける ### ベット額の入力 アクションボタンの上にある「ベット額」入力欄で金額を指定します。ベットまたはレイズ時にこの金額が使用されます。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `c` | コール | アクションフェーズ(ベットがある場合) | | `r` | レイズ / ベット | アクションフェーズ | | `k` | チェック | アクションフェーズ(ベットがない場合) | | `f` | フォールド | アクションフェーズ | | `a` | オールイン | アクションフェーズ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ フェーズ: フロップ ポット: 60 SB/BB: 5/10 ディーラー: Player 0 │ ├────────────────────────────────────────────┤ │ コミュニティカード │ │ [♠7] [♣10] [♥3] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ CPU 1 (TAG) チップ:980 ベット:20 │ │ [🂠] [🂠] │ │ CPU 2 (LAP) チップ:990 [フォールド] │ │ [🂠] [🂠] │ │ CPU 3 (GTO) チップ:960 ベット:10 │ │ [🂠] [🂠] │ ├────────────────────────────────────────────┤ │ CPU行動ログ │ │ Player 1: コール │ │ Player 2: フォールド │ │ Player 3: チェック │ ├────────────────────────────────────────────┤ │ フッター: あなたの手札 │ │ チップ:970 ベット:20 │ │ [♠A] [♥K] │ │ │ │ メッセージエリア │ │ │ │ ベット額: [20 ▲▼] │ │ [コール] [レイズ] [フォールド] [オールイン] │ │ [リセット] │ └────────────────────────────────────────────┘ ``` - **情報バー(上部)**: 現在のフェーズ、ポット額、SB/BB額、ディーラー位置(トーナメントモード時はハンド数とレベルアップ情報も表示) - **コミュニティカード**: 場に公開された共有カード(フェーズに応じて0/3/4/5枚) - **CPUプレイヤーエリア**: 各CPUのプレイスタイル、チップ、ベット額、状態 - ショーダウン時にはCPUのホールカードと役名が表示される - **CPU行動ログ**: CPUが行ったアクションの記録 - **結果エリア**: ショーダウン後、各プレイヤーの役と獲得チップを表示 - **フッター(あなたの手札)**: - ホールカード2枚が表示される - フォールド/オールイン状態のバッジ - ショーダウン時には役名が表示される - **メッセージエリア**: ゲームの状態メッセージ - **アクションボタン**: 手番時のみ表示 ## HUD統計 各プレイヤーにはHUD(ヘッズアップディスプレイ)統計が表示されます: - **VPIP** (Voluntarily Put $ In Pot): プリフロップで自発的にチップを入れた割合(コール/ベット/レイズ/オールイン) - **PFR** (Pre-Flop Raise): プリフロップでレイズした割合(ベット/レイズ/オールイン) 統計はハンド数が1以上のプレイヤーに対してのみ表示されます。 ## トーナメントモード トーナメントモードを有効にすると、指定ハンド数ごとにブラインドが自動的に上昇します。情報バーにハンド数とレベルアップ情報が追加表示されます。 API経由で以下のパラメータを指定できます: - **tournamentMode**: `true` でトーナメントモードを有効化 - **blindLevelHands**: ブラインドレベルアップまでのハンド数(デフォルト10) - **blindMultiplier**: ブラインド倍率(百分率、デフォルト200 = 2倍) - **bettingLimit**: ベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit、デフォルト0) - **tableSize**: テーブルサイズ(4=4-max、6=6-max、9=9-max、デフォルト4) - **rebuyEnabled**: `true` でリバイを有効化(デフォルト`false`) - **rebuyMaxCount**: リバイの最大回数(デフォルト3) - **rebuyChips**: リバイで得られるチップ数(デフォルト1000) - **rebuyPeriodHands**: リバイ可能なハンド数(デフォルト10) - **addonEnabled**: `true` でアドオンを有効化(デフォルト`false`) - **addonChips**: アドオンで得られるチップ数(デフォルト1500) - **addonAfterHand**: アドオン可能になるハンド番号(デフォルト10) - **cpuMetaAI**: `true` でメタAIを有効化(CPUが人間のブラフ率・フォールド率・思考時間を学習して戦略を適応、デフォルト`false`) - **humanPlayMs**: 人間のアクションにかかった時間(ミリ秒)。メタAI有効時にリクエストに含めて送信 ### レスポンスのベッティングリミット関連フィールド | フィールド | 型 | 説明 | |------------|------|------| | `bettingLimit` | int | 現在のベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit) | | `raiseCount` | int | 現在のベッティングラウンドでのレイズ回数 | | `maxBetAmount` | int | 現在のベッティングリミットに基づく最大ベット額 | | `tableSize` | int | 現在のテーブルサイズ(4/6/9) | | `rebuyPhaseType` | int | リバイフェーズの種類(1=リバイ、2=アドオン) | | `rebuyChips` | int | リバイで得られるチップ数 | | `rebuyMaxCount` | int | リバイの最大回数 | | `rebuyCounts` | int[] | 各プレイヤーのリバイ使用回数 | | `addonChips` | int | アドオンで得られるチップ数 | | `muckAvailable` | bool | マック選択が可能かどうか(ショーダウンで負けた場合にtrue) | ## マック/ショー ショーダウンでプレイヤーが負けた場合、手札の公開/非公開を選択できます。 - **「マック」ボタン**: 手札を伏せて非公開にする - **「ショー」ボタン**: 手札を公開する マックした場合、結果表示で手札が非表示になります。 ## リバイ/アドオン ### リバイ リバイ期間内にチップが0になった場合、リバイ画面が表示されます。 - **「リバイ」ボタン**: チップを再購入して続行 - **「スキップ」ボタン**: リバイせずに続行 リバイの有効/無効はフッターの「リバイ有効」チェックボックスで切り替えます。 ### アドオン 指定ハンド数経過後にアドオン画面が表示されます(1回のみ)。 - **「アドオン」ボタン**: チップを追加購入 - **「スキップ」ボタン**: アドオンせずに続行 アドオンの有効/無効はフッターの「アドオン有効」チェックボックスで切り替えます。 CPUプレイヤーはリバイ・アドオンを自動的に実行します。 ## ラーニングモード 画面下部の「ラーニングモード」チェックボックスを有効にすると、リアルタイムのエクイティ(勝率)とポットオッズが表示されます。 ### 表示内容 - **エクイティ**: モンテカルロシミュレーション(5000回)による勝率。緑色のバーで視覚的に表示されます - **ポットオッズ**: コール額 / (ポット + コール額) × 100。コールに必要な勝率の目安です - **+EV / -EV インジケーター**: エクイティがポットオッズを上回る場合は +EV(期待値プラス = コールが有利)、下回る場合は -EV(期待値マイナス = フォールドが有利)と表示されます - **ハンドオッズ**: クリックで展開。現在の手札とコミュニティカードから、各ハンドランク(ワンペア、フラッシュ等)になる確率を表示します ### 使い方 エクイティとポットオッズを比較して判断します: - エクイティ > ポットオッズ → コール/レイズが期待値的に有利 - エクイティ < ポットオッズ → フォールドが期待値的に有利 ラーニングモードはプリフロップからリバーまでのアクティブフェーズで利用できます。ショーダウン・終了フェーズでは表示されません。 ## 遊び方のコツ - ポジション(ディーラーからの距離)は重要です。後ろのポジションほど有利です - 強い手(ペア、高いカード)はプリフロップでレイズして主導権を握りましょう - LAGスタイルのCPUはブラフが多いので、良い手を持っているときはコールやレイズで対抗しましょう - GTOスタイルのCPUは確率的な混合戦略を使うため、行動パターンが読みにくいです。ボードテクスチャに注意して対応しましょう - TAGスタイルのCPUがレイズしてきたら、本当に強い手を持っている可能性が高いので注意しましょう - HUD統計を活用して、CPUのプレイスタイルの傾向を把握しましょう - トーナメントモードではブラインドが上がるため、早めにチップを稼ぐ戦略が重要です - コミュニティカードとの組み合わせを常に確認し、フラッシュやストレートの可能性を見逃さないようにしましょう - オールインは最後の手段として使いましょう。全チップを失うリスクがあります # インディアンポーカー(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶベッティング型カードゲームです。各プレイヤーに1枚ずつカードが配られ、自分のカードは見えず相手のカードだけが見える状態でベッティングを行います。カードのランクが最も高いプレイヤーがポットを獲得します。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「インディアンポーカー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のカードから各プレイヤーに1枚ずつ配布 - 自分のカードは見えない(額に貼り付けるイメージ) - 他のプレイヤーのカードは見える - カードのランクで勝敗を決定(2が最弱、Ace=14が最強) - 各ラウンド開始時にアンティ(参加費)を投入 ### ベッティング - **フォールド**: 降りる(投入済みチップは失う) - **チェック**: パスする(未ベット時のみ) - **コール**: 現在のベット額に合わせる - **ベット**: 最初の賭け金を出す - **レイズ**: 現在のベット額を引き上げる - **オールイン**: 全チップを賭ける ### ベッティングリミット - **Fixed**: 固定額ベット - **Pot Limit**: ポット額まで - **No Limit**: 上限なし(デフォルト) ### ゲーム終了 - いずれかのプレイヤーのチップが0になった場合にゲーム終了 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[アンティ投入・カード配布] C --> D{プレイヤーの手番?} D -->|はい| E[アクションボタンで選択] D -->|いいえ| F[CPUが自動でアクション] E --> G{ベッティング終了?} F --> G G -->|いいえ| D G -->|はい| H[ショーダウン - カード公開・勝者決定] H --> I{ゲーム終了条件を満たした?} I -->|いいえ| C I -->|はい| J[ゲーム終了 - 結果表示] J -->|リセットボタン| B ``` ## 画面の操作方法 ### ベッティングフェーズ 1. 他のプレイヤーのカードを確認します(自分のカードは「?」で表示) 2. 以下のアクションボタンから選択します: - **「フォールド」**: 降りる - **「チェック」**: パスする(ベットがない場合) - **「コール」**: 現在のベットに合わせる - **「ベット」**: 金額入力後にベットする - **「レイズ」**: 金額入力後にレイズする - **「オールイン」**: 全チップを賭ける ### ショーダウン 1. 全プレイヤーのカードが公開されます 2. 勝者とチップの移動が表示されます 3. 自動的に次のハンドが開始されます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ アンティ: [10] │ │ ベッティング: [No Limit▼] │ │ メタAI: [ON] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 920チップ│ │ 680チップ│ │ 550チップ│ │ │ │ ♥K │ │ ♠5 │ │ ♦10 │ │ │ │ ベット:30│ │ フォールド│ │ ベット:30│ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ハンド #5 | ポット: 120 | アンティ: 10 │ ├────────────────────────────────────────────┤ │ フッター: あなた (850チップ) │ │ [???] (自分のカードは見えない) │ │ [フォールド] [チェック] [コール] │ │ [ベット] [レイズ] [オールイン] │ │ [リセット] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「アンティ」: アンティ額を設定 - 「ベッティング」: Fixed / Pot Limit / No Limit から選択 - 「メタAI」: CPUのメタAI学習を有効/無効に切り替え - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU のチップ、カード(表向き)、現在のベット状態 - 手番の CPU は金色枠 - **ポット情報**: 現在のハンド番号、ポット合計、アンティ額 - **フッター(あなたのカード)**: - ベッティング中は「???」表示(自分のカードが見えない) - ショーダウン時にカードが公開される ## 遊び方のコツ - 他のプレイヤーのカードを観察し、自分のカードの強さを推測しましょう - 相手のカードが弱い場合、自分のカードが強い可能性が高いのでベットしましょう - CPUのベッティングパターンから自分のカードの強さを推測できます - オールインは慎重に。チップが0になるとゲーム終了です - メタAIが有効な場合、CPUはあなたのプレイパターンを学習して戦略を調整します # ジョーカーポーカー(Web版)遊び方 ## ゲーム概要 1人用カジノカードゲームです。53枚のトランプ(52枚+ジョーカー1枚)を使い、5枚のカードでポーカーの役を作ります。ジョーカーがワイルドカードとして扱われ、他の任意のカードの代わりになります。Joker Poker専用のペイテーブルに基づいて配当が支払われます。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ジョーカーポーカー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 53枚のトランプ(52枚+ジョーカー1枚)を使用 - ジョーカーがワイルドカード - 1〜5コインをベットしてゲーム開始 - 5枚のカードが配られる - 残したいカード(ホールド)を選び、残りを交換 - 最終的な5枚の手札で役を判定し、配当を支払い ### 配当表(Joker Poker) | 役 | 1コイン | 2コイン | 3コイン | 4コイン | 5コイン | |----|---------|---------|---------|---------|---------| | ナチュラルロイヤルフラッシュ | 250 | 500 | 750 | 1000 | 4000 | | ファイブカード | 200 | 400 | 600 | 800 | 1000 | | ワイルドロイヤルフラッシュ | 100 | 200 | 300 | 400 | 500 | | ストレートフラッシュ | 50 | 100 | 150 | 200 | 250 | | フォーカード | 20 | 40 | 60 | 80 | 100 | | フルハウス | 7 | 14 | 21 | 28 | 35 | | フラッシュ | 5 | 10 | 15 | 20 | 25 | | ストレート | 3 | 6 | 9 | 12 | 15 | | スリーカード | 2 | 4 | 6 | 8 | 10 | | ツーペア | 1 | 2 | 3 | 4 | 5 | | キングスオアベター | 1 | 2 | 3 | 4 | 5 | ### ワイルドカード - ジョーカーは任意のカードとして扱える - ナチュラルロイヤルフラッシュ: ワイルドなしのロイヤルフラッシュ(最高配当) - ワイルドロイヤルフラッシュ: ジョーカーを含むロイヤルフラッシュ - ファイブカード: ジョーカーを使って同じランク5枚 ### キングスオアベター - K、Aのワンペア以上が配当の最低条件 - Q以下のワンペアは配当なし(ツーペアは配当あり) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[ベットフェーズ] B --> C[コイン数を選択] C --> D[ベットボタンをクリック] D --> E[5枚のカードが表示される] E --> F[ドローフェーズ] F --> G[ホールドするカードをクリックで選択] G --> H[ドローボタンをクリック] H --> I[交換後のカードと役判定・配当を表示] I -->|リセットボタン| B ``` ## 画面の操作方法 ### ベットフェーズ 1. **コイン数選択**: コインセレクター(1〜5)でベット額を設定 2. **「ベット」ボタン**: クリックでベットを確定し、5枚のカードが配られる ### ドローフェーズ 1. **カード選択**: ホールドしたいカードをクリックして選択(選択したカードはハイライト表示) 2. **再クリック**: 選択を解除 3. **「ドロー」ボタン**: ホールドしていないカードを交換し、役を判定 ### 結果フェーズ 1. 最終的な5枚のカードと役名が表示されます 2. 配当額が表示されます 3. **「リセット」ボタン**: 新しいラウンドを開始 4. **「棋譜を見る」ボタン**: アクションログを表示 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `b` | ベット | ベットフェーズのみ | | `d` | ドロー | ドローフェーズのみ | | `r` | リセット | 結果フェーズのみ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ チップ: 100 │ ├────────────────────────────────────────────┤ │ 配当表 │ │ ナチュラルロイヤルフラッシュ 250 500 750 1000 4000 │ │ ... │ │ キングスオアベター 1 2 3 4 5 │ ├────────────────────────────────────────────┤ │ │ │ [♠A] [★JK] [♦K] [♣3] [♠J] │ │ HOLD HOLD HOLD │ │ │ │ スリーカード 配当: 6 │ ├────────────────────────────────────────────┤ │ [ドロー] [棋譜を見る] │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ - 5コインベットでナチュラルロイヤルフラッシュの配当が大幅に増える(250→4000)ため、可能なら最大ベットがお得 - ジョーカーは常にホールドする - ジョーカーを持っている場合、スリーカード以上を狙いやすい - ジョーカーなしの場合はキングスオアベター(K以上のペア)が最低配当条件なので注意 - ツーペアも配当があるため、ペアが2つある場合はホールドする # クロンダイク(Web版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、7列のタブロー、山札、ウェスト、4つの組札で構成されます。組札にA→Kまでスート別に積み上げればクリアです。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「クロンダイク」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 7列のタブローに左から1枚、2枚、...、7枚ずつカードを配る(各列の一番上のカードのみ表向き) - 残りの24枚は山札(ストック)に裏向きで置く - 4つの組札(ファンデーション)はスート別にA→Kの順に積み上げる - タブローでは降順かつ交互の色で重ねる(例: 黒のKの上に赤のQ) ### ゲームクリア - 4つの組札すべてにA→Kまで13枚ずつ積み上げるとゲームクリア ### ゲームオーバー - これ以上移動可能な手がない場合はゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札とウェストが空の場合、または山札を一巡しても進展がなかった場合に「手詰まりです」とメッセージが表示されます - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カードを配る] C --> D{移動可能な手がある?} D -->|はい| E{プレイヤーのアクション} E -->|山札クリック| F[山札からウェストにカードをめくる] E -->|カードをドラッグ/クリック| G[カードを移動] E -->|ヒントボタン| H[ヒントを表示] E -->|オートコンプリートボタン| I[自動で組札に積み上げ] F --> D G --> J{組札が完成?} H --> D I --> J J -->|いいえ| D J -->|はい| K[ゲームクリア] D -->|いいえ| L[ゲームオーバー] K -->|リセットボタン| B L -->|リセットボタン| B ``` ## 画面の操作方法 ### カードを移動する 1. 移動元のカードをクリックして選択します 2. 移動先のカード(またはエリア)をクリックして移動を確定します 3. タブロー内では表向きの連続したカード列をまとめて移動できます ### 山札をめくる 1. 山札エリアをクリックしてカードをウェストにめくります(1枚引きモードでは1枚、3枚引きモードでは3枚) 2. 3枚引きモードでは、めくったカードのうち一番上のカードのみ操作可能です 3. 山札が空になった場合、再度クリックするとウェストのカードが山札に戻ります ### ドローモード切替 フッターの「ドローモード」セレクトで1枚引き / 3枚引きを切り替えられます。切替時にゲームがリセットされます。 ### アンドゥ(元に戻す) **「元に戻す」** ボタンまたは `z` キーで直前の操作を取り消せます。何度でも元に戻せます。 ### スコアモード フッターの「スコアモード」セレクトで「なし」/「ベガス」を切り替えられます。 - **ベガス**: スコア = -52 + 5 × 組札のカード枚数。クリア時にはタイムボーナスも加算されます。 ### ヒント・オートコンプリート 1. **「ヒント」** ボタンをクリックすると、次に可能な手を提案します 2. **「オートコンプリート」** ボタンをクリックすると、組札に安全に移動できるカードを自動で移動します ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `d` | カードをめくる(ドロー) | プレイ中 | | `z` | 元に戻す(アンドゥ) | プレイ中 | | `h` | ヒント | プレイ中 | | `a` | オートコンプリート | プレイ中 | | `g` | ギブアップ | プレイ中 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ゲーム情報 │ │ 手数: 15 スコア: -27 タイム: 02:15 │ ├────────────────────────────────────────────┤ │ 山札・ウェストエリア 組札エリア │ │ [山札] [♠K] [♠A][♥A2][ ][ ] │ ├────────────────────────────────────────────┤ │ タブロー │ │ [1] [2] [3] [4] [5] [6] [7]│ │ ♥Q ?? ?? ?? ?? ?? ?? │ │ ♣J ?? ?? ?? ?? ?? │ │ ♦10 ?? ?? ?? ?? │ │ ♠9 ?? ?? ?? │ │ ♥8 ?? ?? │ │ ♣7 ?? │ │ ♦6 │ ├────────────────────────────────────────────┤ │ [引く] [元に戻す] [ヒント] [オートコンプリート]│ │ [ギブアップ] [ドローモード▼] [スコアモード▼]│ │ [リセット] │ └────────────────────────────────────────────┘ ``` - **山札エリア**: クリックでカードをめくる(残りカード数表示) - **ウェストエリア**: めくられたカードが表示される - **組札エリア**: 4つのスート別の組札(A→Kの順に積み上げ) - **タブロー**: 7列のカード列 - 裏向きカード: 直接操作不可 - 表向きカード: クリックで選択・移動 - **ボタン**: - 「引く」: 山札からカードをめくる - 「元に戻す」: 直前の操作を取り消す - 「ヒント」: 次の一手を提案 - 「オートコンプリート」: 自動で組札へ移動 - 「ギブアップ」: 現在のゲームを終了 - 「ドローモード」: 1枚引き / 3枚引きの切替 - 「スコアモード」: なし / ベガスの切替 - 「リセット」: 新しいゲームを開始 ## 遊び方のコツ - まずタブローの裏向きカードをめくることを優先しましょう - Aが出たらすぐに組札へ移動しましょう - 空いた列にはKのみ置くことができます - 山札を何度もめくり直して使えるカードを探しましょう - ヒントボタンで詰まった時のアドバイスを得られます - オートコンプリートは組札に安全に移動できるカードがある場合に便利です # 神経衰弱(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。52枚のカードを裏向きに並べ、2枚ずつめくって同じランクのペアを揃えます。全26ペアが取られた時点で最もペア数の多いプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「神経衰弱」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を裏向きにボード上に並べる - プレイヤーは順番に2枚のカードをめくる - めくった2枚が同じランクならペアとして獲得し、もう1ターン続行 - 異なるランクなら2枚を裏に戻し、次のプレイヤーに交代 ### ゲーム終了 - 全26ペアが取られた時点でゲーム終了 - 最もペア数の多いプレイヤーが勝者 ### CPU難易度 - **Easy**: ランダムにカードをめくる - **Normal**: 部分的にカードの位置を記憶する(デフォルト) - **Hard**: 完全にカードの位置を記憶する ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[52枚のカードを裏向きに配置] C --> D{プレイヤーの手番?} D -->|はい| E[1枚目のカードをクリック] D -->|いいえ| F[CPUが自動でカードをめくる] E --> G[2枚目のカードをクリック] G --> H{同じランク?} F --> H H -->|はい| I[ペアを獲得] I --> J{全ペア取得済み?} J -->|いいえ| D H -->|いいえ| K[次へボタンをクリック] K --> L{全ペア取得済み?} L -->|いいえ| M[次のプレイヤーへ] M --> D J -->|はい| N[ゲーム終了 - 勝者表示] L -->|はい| N N -->|リセットボタン| B ``` ## 画面の操作方法 ### カードをめくる(Flip1・Flip2フェーズ) 1. ボード上の裏向きのカードをクリックして1枚目をめくります 2. 続けて2枚目の裏向きのカードをクリックしてめくります 3. 同じランクならペアとして獲得し、続けてもう1ターンプレイできます ### 結果確認後の進行(Resultフェーズ) 1. めくった2枚が異なるランクの場合、結果が表示されます 2. **「次へ」** ボタンをクリックしてカードを裏に戻し、次のプレイヤーに交代します ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `n` | 次へ | 結果フェーズ(Resultフェーズ) | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 2ペア │ │ 1ペア │ │ 0ペア │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ 残りペア: 20 / 26 │ │ 手番: あなた │ ├────────────────────────────────────────────┤ │ ボード │ │ [??][??][??][♠2][??][??][--] │ │ [??][??][??][??][??][??][--] │ │ ... │ ├────────────────────────────────────────────┤ │ フッター: あなた (3ペア) │ │ [リセット] [次へ] │ └────────────────────────────────────────────┘ ``` - **CPU プレイヤーエリア**: 各 CPU の獲得ペア数を表示 - 手番の CPU は金色枠 - **ゲーム情報**: 残りペア数、現在の手番プレイヤー - **ボード**: - 裏向きカード: クリックでめくる - 表向きカード: めくられたカードが表示される - 取得済みカード: 空白または非表示 - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 次のリセット時に設定が適用される ## 遊び方のコツ - めくられたカードの位置とランクを覚えておきましょう - CPUがめくったカードも注意深く観察すると、ペアの位置を特定しやすくなります - 序盤は情報収集を重視し、まだ見ていない位置のカードを優先的にめくると効率的です - 確実にペアだとわかるカードがあれば、優先的にめくりましょう # ナポレオン(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。52枚+ジョーカー1枚の計53枚を使用し、絵札(J/Q/K/A + ジョーカー = 最大17枚)の獲得数をビッドして競います。最高ビッドのプレイヤーがナポレオンとなり、切り札スートと副官カードを宣言します。ナポレオン軍(ナポレオン+副官)と連合軍の隠しチーム戦です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ナポレオン」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 53枚のカード(52枚+ジョーカー1枚)を4人に配布(1人13枚、余り1枚はキティ) - 各ラウンド開始時に絵札の獲得予想数をビッド(最低入札数〜17) - 最高ビッドのプレイヤーが **ナポレオン** になる - ナポレオンは **切り札スート** を宣言し、 **副官カード** を指名する - 副官カードを持つプレイヤーはナポレオンの味方(他のプレイヤーには非公開) - ナポレオンはキティのカードと手札を交換できる - リードスートに従う(フォロースート) - トリックの勝者: 切り札が出ていれば最高の切り札、なければリードスートの最高値 ### 特殊カード - **ジョーカー(マイティ)**: 最も強いカード。どのトリックでも勝つ - **スペードの3(ジョーカーキラー)**: ジョーカーに対してのみ勝てる特殊カード ### 絵札(ピクチャーカード) 以下のカードが絵札としてカウントされます(最大17枚): - ジャック (J) × 4枚 - クイーン (Q) × 4枚 - キング (K) × 4枚 - エース (A) × 4枚 - ジョーカー × 1枚 ### 得点 - **ナポレオン軍の勝利**: ナポレオン軍が獲得した絵札数 >= ビッド数 - **連合軍の勝利**: ナポレオン軍が獲得した絵札数 < ビッド数 ### ゲーム終了 - ポイント上限に到達したプレイヤーが出た場合にゲーム終了 ### CPU難易度 - **Easy**: ランダムにカードを出す - **Normal**: 基本的なトリック戦略(デフォルト) - **Hard**: 戦略的なプレイ(切り札管理等) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各13枚 + キティ1枚] C --> D[ビッドフェーズ] D --> E[ビッド数を入力してビッドボタン / パスボタン] E --> F[ナポレオン決定] F --> G[切り札スートを選択] G --> H[副官カードを指名] H --> I[ナポレオンがキティと手札を交換] I --> J{プレイヤーの手番?} J -->|はい| K[手札のカードをクリックして選択] J -->|いいえ| L[CPUが自動でカードを出す] K --> M[出すボタンをクリック] M --> N[トリック完了] L --> N N -->|次のトリックボタン| O{13トリック終了?} O -->|いいえ| J O -->|はい| P[ラウンド終了 - 絵札集計・得点計算] P -->|次のラウンドボタン| Q{ゲーム終了条件を満たした?} Q -->|いいえ| C Q -->|はい| R[ゲーム終了 - 結果表示] R -->|リセットボタン| B ``` ## 画面の操作方法 ### ビッドフェーズ 1. 数値入力欄にビッド数(最低入札数〜17)を入力します 2. **「ビッド」** ボタンで確定します 3. ビッドしない場合は **「パス」** ボタンをクリックします ### 切り札宣言(ナポレオンのみ) 1. スート選択ボタン(スペード/クローバー/ハート/ダイヤ)から切り札を選択します ### 副官カード指名(ナポレオンのみ) 1. 副官として指名するカードを選択します ### キティ交換(ナポレオンのみ) 1. キティのカードが表示されます 2. 手札から交換したいカードを選択します 3. **「交換」** ボタンで確定します ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します 2. フォロー必須のルールに従い、出せないカードは選択できません 3. **「カードを出す」** ボタンをクリックしてカードを場に出します ### ヒント - プレイフェーズで **「ヒント」** ボタンをクリックすると、推奨カードが表示されます ### トリック・ラウンドの進行 1. トリックが完了すると結果が表示されます 2. **「次のトリック」** ボタンで次のトリックに進みます 3. ラウンドが完了するとナポレオン軍・連合軍の絵札集計と得点が表示されます 4. **「次のラウンド」** ボタンで次のラウンドに進みます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | プレイヤーの手番 | | `Enter` | カードを出す / ビッド確定 | プレイヤーの手番 / ビッドフェーズ | | `Escape` | 選択クリア | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ 目標スコア: [300▼] │ │ 最低入札数: [12▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 80点 │ │ 30点 │ │ 40点 │ │ │ │ ナポレオン│ │ 絵札:2 │ │ 絵札:1 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 2 / トリック 5 │ │ 切り札: スペード │ │ ナポレオン: CPU 1 (ビッド: 12) │ │ 副官カード: ♥A │ ├────────────────────────────────────────────┤ │ 現在のトリック │ │ CPU 1: ♣10 CPU 2: ♣K │ ├────────────────────────────────────────────┤ │ フッター: あなた (50点, 絵札:3) │ │ [♠5][♣8][♥K][♦3]... │ │ [リセット] [ヒント] [カードを出す] [次のトリック] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「目標スコア」: ポイント上限を設定 - 「最低入札数」: 最低入札数を設定 - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU の累計得点、獲得絵札数、ナポレオン/副官の表示 - 手番の CPU は金色枠 - **ゲーム情報**: 現在のラウンド、トリック番号、切り札スート、ナポレオン情報、副官カード - **現在のトリック**: トリックに出されたカード - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - フォロー必須のカードのみ選択可能 ## 遊び方のコツ - ビッドは手札の強さに応じて慎重に行いましょう。絵札とトランプの枚数を確認してからビッドしましょう - ナポレオンになった場合、切り札スートは自分が多く持つスートを選びましょう - 副官カードは、自分が持っていない強いカード(例: エース)を指名すると効果的です - ジョーカー(マイティ)は最強のカードですが、スペードの3(ジョーカーキラー)に注意しましょう - 連合軍の場合、ナポレオンの切り札を早く消費させる戦略が有効です - 迷った時はヒントボタンで推奨カードを確認できます # オー・ヘル(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。ラウンドごとに手札枚数が変動し、各ラウンドでビッド(獲得予想トリック数)を宣言します。全ラウンド終了後、累計得点が最も高いプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「オー・ヘル」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを4人に配布(手札枚数はラウンドごとに変動) - 配り終わった山札の一番上のカードが**切り札(トランプ)スート**を決定 - 山札が残らないラウンドでは切り札なし - 各ラウンド開始時にビッド(0〜手札枚数)を宣言 - **ディーラー制限(フック)**: ディーラーは全員のビッド合計が手札枚数と同じになるビッドを選べない - リードスートに従う(フォロースート) - トリックの勝者: 切り札が出ていれば最高の切り札、なければリードスートの最高値 ### 得点 - **ビッド成功(正確に一致)**: 10 + ビッド数 - **ビッド失敗**: 0点(スタンダード方式)または -|差分|点(ペナルティ方式) ### ゲーム終了 - 全ラウンド終了後、累計得点が最も高いプレイヤーが勝利 ### CPU難易度 - **Easy**: ランダムにビッド・カードを出す - **Normal**: 基本的なトリック戦略(デフォルト) - **Hard**: 戦略的なプレイ ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 手札枚数はラウンドにより変動] C --> D[切り札カード公開] D --> E[ビッドフェーズ] E --> F[ビッド数を入力してビッドボタン] F --> G{プレイヤーの手番?} G -->|はい| H[手札のカードをクリックして選択] G -->|いいえ| I[CPUが自動でカードを出す] H --> J[出すボタンをクリック] J --> K[トリック完了] I --> K K -->|次のトリックボタン| L{全トリック終了?} L -->|いいえ| G L -->|はい| M[ラウンド終了 - 得点計算] M -->|次のラウンドボタン| N{全ラウンド終了?} N -->|いいえ| C N -->|はい| O[ゲーム終了 - 結果表示] O -->|リセットボタン| B ``` ## 画面の操作方法 ### ビッドフェーズ 1. 数値入力欄にビッド数(0〜手札枚数)を入力します 2. **「ビッド」** ボタンで確定します 3. ディーラーの場合、制限されたビッド値は選択できません(フック制限) ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します 2. フォロー必須のルールに従い、出せないカードは選択できません 3. **「カードを出す」** ボタンをクリックしてカードを場に出します ### ヒント - ビッドフェーズまたはプレイフェーズで **「ヒント」** ボタンをクリックすると、CPUのHard戦略に基づく推奨が表示されます - ビッドフェーズでは推奨ビッド数、プレイフェーズでは推奨カードが黄色のメッセージで表示されます ### トリック・ラウンドの進行 1. トリックが完了すると結果が表示されます 2. **「次のトリック」** ボタンで次のトリックに進みます 3. ラウンドが完了すると得点が表示されます 4. **「次のラウンド」** ボタンで次のラウンドに進みます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | プレイヤーの手番 | | `Enter` | カードを出す / ビッド確定 | プレイヤーの手番 / ビッドフェーズ | | `Escape` | 選択クリア | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ 最大手札枚数: [10▼] │ │ スコアリング: [Standard▼] │ │ ラウンド方向: [DownAndUp▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 20点 │ │ 40点 │ │ 10点 │ │ │ │ B:3 W:2 │ │ B:1 W:1 │ │ B:2 W:0 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 3/19 / トリック 2 (手札 8枚) │ │ 切り札: ♥ (♥K) ディーラー: CPU 2 │ ├────────────────────────────────────────────┤ │ 現在のトリック │ │ CPU 1: ♣10 CPU 2: ♣K │ ├────────────────────────────────────────────┤ │ フッター: あなた (30点, B:2 W:1) │ │ [♠5][♣8][♥K][♦3]... │ │ [リセット] [ヒント] [カードを出す] [次のトリック] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「最大手札枚数」: 1〜13 から選択 - 「スコアリング」: Standard / Penalty から選択 - 「ラウンド方向」: DownOnly / DownAndUp から選択 - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU の累計得点、ビッド数、獲得トリック数を表示 - 手番の CPU は金色枠 - **ゲーム情報**: 現在のラウンド/全ラウンド数、トリック番号、手札枚数、切り札スート、ディーラー - **現在のトリック**: トリックに出されたカード - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - フォロー必須のカードのみ選択可能 - **スコアテーブル**: 各プレイヤーのビッド、獲得トリック数、ラウンドスコア、累積スコア ## 遊び方のコツ - ビッドは手札の強さ(切り札の枚数、高いカード)を考慮して慎重に宣言しましょう - ディーラーの場合はフック制限を意識してビッドしましょう - 切り札が少ないラウンドではビッドを控えめにするのが安全です - 手札枚数が少ないラウンドほど運の要素が強くなります - 正確なビッドが高得点の鍵です(多くても少なくてもダメ) - 迷った時はヒントボタンでCPUのHard戦略に基づく推奨を確認できます # ババ抜き / ジジ抜き(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。ペアを揃えて手札をなくしていき、最後に「負け札」を持っているプレイヤーが負けです。 - **ババ抜き(ノーマルモード)**: ジョーカーが負け札 - **ジジ抜き**: ゲーム開始前にランダムな1枚が取り除かれ、その対となるカードが負け札(取り除かれたカードはゲーム終了時に公開) ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「Old Maid」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール(共通) - 全カードを4人に配布し、最初に手札内のペア(同じ数字2枚)を全て捨てる - 順番に次のプレイヤーの手札から1枚引き、ペアができたら捨てる - 手札がなくなったプレイヤーから上がり - 最後に「負け札」が残ったプレイヤーが負け ### モード別ルール | モード | 使用カード | 負け札 | |--------|-----------|--------| | ババ抜き(mode=0) | 52枚 + ジョーカー1枚(計53枚) | ジョーカー | | ジジ抜き(mode=1) | 52枚からランダムに1枚除去(計51枚) | 除去されたカードのペアとなるカード | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> S[モード選択画面] S -->|ババ抜き / ジジ抜きを選択| B[自動でゲーム開始] B --> C[カード配布 + ペア除去] C --> D[CPUが自動で手番を進行] D --> E{プレイヤーの手番?} E -->|はい| F[相手のカードをクリックして引く] E -->|いいえ| G[CPUが自動でカードを引く] G --> E F --> H[ペアがあれば自動で捨てる] H --> I[CPU行動をアニメーション再生] I --> J{ゲーム終了?} J -->|いいえ| E J -->|はい| K[結果表示] K -->|リセットボタン| B K -->|設定ボタン| S ``` ## 画面の操作方法 ### モード選択(ゲーム開始前) 画面を開くと最初にモード選択画面が表示されます。 | 選択肢 | mode値 | 説明 | |--------|--------|------| | ババ抜き | 0 | ジョーカーが負け札(ノーマルモード) | | ジジ抜き | 1 | ランダムな1枚を除去し、その対となるカードが負け札 | - **CPU心理戦** チェックボックス: 有効にすると、CPUは「負け札」を手札の端(最初または最後)に配置します。ゲーム中、CPU心理戦が有効な場合は負け札の疑いがあるカードが少し上にずれて表示されます。 - **CPU記憶AI** チェックボックス: 有効にすると、CPUは前回引いた位置を記憶し、戦略的にカードを選択します。 - 前回ペアが不成立だった位置を避ける傾向(40%) - 前回ペアが成立した位置の近傍(±1)を好む傾向(40%) - 人間がシャッフルや並べ替えをした場合、記憶がリセットされ端を選ぶ確率が上がります(50%) - **Meta-AI** チェックボックス: 有効にすると、CPUがセッション内のゲーム履歴(プレイヤーの端カード選択率)を学習し、戦略を適応的に調整します。 - **CPU迷い時間ディレイ** チェックボックス: 有効にすると、CPUがカードを引いた後の表示ディレイが引いたカードの内容に応じて変化します。 - ジョーカーを引いた場合: 1000–1500ms(遅い — 悪いカードを引いた動揺) - ペアが成立した場合: 300–500ms(速い — 嬉しい反応) - 通常: 600–1000ms(中間) - モード・オプションを選択後、「ゲーム開始」ボタンを押すとゲームが開始されます。 ### ゲーム中の表示 - **ジジ抜きモードバッジ**: ジジ抜きでプレイ中は画面上部に赤い「ジジ抜き」バッジが表示されます。 - **ハイライトカード**: CPU心理戦が有効な場合、負け札の疑いがあるCPUのカードが他のカードより少し上にずれて表示されます(`cpuHighlightedCardIdx` が -1 以外のとき)。 - **取り除かれたカードの公開**: ジジ抜きモードでゲームが終了すると、ゲーム開始時に取り除かれたカードが公開されます(`removedCard`)。 - **設定ボタン**: ゲーム終了後に「設定」ボタンを押すとモード選択画面に戻ります。 ### 引き履歴タイムライン ゲーム中、画面に「引き履歴」パネルが表示されます。全プレイヤーの引きの履歴を番号付きで時系列に確認できます。 - 各エントリには「誰が誰から引いたか」「捨てたペア数」が表示されます - 引いた結果上がったプレイヤーには「上がり」が付記されます - カードの内容は表示されません(プライバシー設計) - 新しいエントリが追加されると自動的にスクロールします - ゲームリセット・設定画面への遷移で履歴はクリアされます ### 容疑者ピン CPUプレイヤーに「負け札を持っている疑い」のマークを付けることができます。 - CPUプレイヤーのエリアにある「容疑者ピン」ボタンを押すと、そのCPUに赤い「容疑者」バッジが表示されます - もう一度押すと解除されます - ピンは純粋にクライアント側のメモ機能で、ゲームロジックには影響しません - ゲームリセット・設定画面への遷移でピンはクリアされます ### プレイヤーの手番 1. 引く相手のCPUパネルが金色の枠で強調され、「← 引く相手」バッジが表示されます 2. そのCPUの手札が裏向きカード(CardBack)として表示されます 3. **裏向きカードをクリック**してカードを引きます 4. または **「ランダムに引く」** ボタンでランダムに1枚引きます ### 手札の並べ替え CPUは手札の位置を参考にカードを引きます(30%の確率で端のカードを優先)。自分の手札を並べ替えることで、CPUに引かせたいカードの位置を戦略的にコントロールできます。 - **ドラッグ&ドロップ**: プレイヤーの手札カードをドラッグして別の位置にドロップすることで、カードの順番を入れ替えられます - **シャッフルボタン**: 「シャッフル」ボタンを押すと、手札をランダムに並べ替えます ### カード引きアニメーション - カードを引いた後、引いたカードは最初に裏面(CardBack)で表示され、約600msの「ため」の後に表向きにフリップして公開されます - ジョーカーを引いた場合、画面全体が震える(シェイク)アニメーションが再生されます ### CPU の手番 CPUの行動は自動で進行し、各アクションが800msの間隔でアニメーション再生されます。 ### 捨てカード表示 最後に捨てたペアが「捨てカード」エリアに表向きで表示されます。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `d` | ランダムに引く | プレイヤーの手番 | | `s` | シャッフル | プレイヤーの手番 | | `←` / `→` | 手札のフォーカス移動 | プレイヤーの手番 | | `Shift` + `←` / `→` | 手札の入れ替え(並べ替え) | プレイヤーの手番 | | `Escape` | フォーカス解除 | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ [ジジ抜き] (モードバッジ) │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 13枚 │ │ 上がり │ │ 12枚 │ │ │ │ [裏][裏↑]│ │ │ │ [裏][裏] │ │ │ │← 引く相手│ │ │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ 捨てカード: [♥9] [♦9] │ ├────────────────────────────────────────────┤ │ [ランダムに引く] [シャッフル] [リセット] [設定] │ ├────────────────────────────────────────────┤ │ 引き履歴タイムライン │ │ 1. Player 1 → CPU 1: 1組捨て │ │ 2. CPU 1 → CPU 2: 0組捨て │ │ 3. CPU 2 → CPU 3: 2組捨て (CPU 2 上がり) │ ├────────────────────────────────────────────┤ │ プレイヤーの手札(ドラッグで並替可) │ │ [♠3] [♥5] [♦9] [♣K] ... │ └────────────────────────────────────────────┘ ``` - **引く相手のCPU**: 金色枠 + 「← 引く相手」バッジ + クリック可能な裏向きカード - **プレイヤーの手札**: 画面下部に表向きで表示(ドラッグ&ドロップで並べ替え可能) - **CPU の手札**: 枚数のみ表示、カード内容は非表示 - **捨てカード**: 最後に捨てたペアが表向きで表示 - **[裏↑]**: CPU心理戦が有効なとき、ハイライトされたカード(負け札の疑いあり)が少し上にずれて表示される - **[ジジ抜き]バッジ**: ジジ抜きモード中に画面上部に赤く表示 - **[設定]ボタン**: ゲーム終了後にモード選択画面へ戻る - **引き履歴タイムライン**: 全プレイヤーの引きの履歴を番号付きで時系列表示 - **容疑者バッジ**: CPUプレイヤーに「容疑者」ピンを付けると赤いバッジが表示される ## 遊び方のコツ - 引く相手の裏向きカードをクリックすることで、位置を指定してカードを引けます - 「ランダムに引く」ボタンでもプレイ可能です - CPUの行動はアニメーションで順次表示されるので、ゲームの進行を見守りましょう - CPUは端のカードを優先して引く傾向があるので、「シャッフル」やドラッグ&ドロップで手札を並べ替え、ジョーカーを端から遠ざけると有利です - 引き履歴タイムラインを見て、誰がどこから引いたか・ペアが成立したかを分析すると、負け札の位置を推理しやすくなります - 容疑者ピンを使って、負け札を持っていそうなCPUをマークしておくと整理しやすくなります # オマハホールデム(Web版)遊び方 ## ゲーム概要 オマハホールデムポーカーのWeb GUI版です。テキサスホールデムの派生ゲームで、各プレイヤーに4枚のホールカードが配られ、必ずホールカードから2枚、コミュニティカードから3枚を使って5枚の役を作ります。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「オマハ」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用 - 各プレイヤーに**4枚**のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - **ホールカードから必ず2枚**、**コミュニティカードから必ず3枚**を使って5枚の役を作る(テキサスホールデムとの最大の違い) - 最も強い役を持つプレイヤーがポットを獲得する ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード4枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 5. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 6. **ショーダウン**: 残ったプレイヤーのハンドを比較 ### ベッティングリミット - **Fixed Limit**: レイズ額が固定 - **Pot Limit**: ポット額までレイズ可能(デフォルト) - **No Limit**: 全チップまでレイズ可能 ### ゲーム終了 - チップが0になったプレイヤーは脱落(トーナメントモードではリバイ可能) - 最後の1人になったらゲーム終了 ### CPU難易度 - 各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持つ - ブラフを含む戦略的なプレイを行う ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[ブラインド強制ベット] C --> D[ホールカード4枚配布] D --> E[プリフロップ - ベッティング] E --> F{全員フォールド?} F -->|はい| L[ポット獲得] F -->|いいえ| G[フロップ - コミュニティ3枚公開] G --> H[フロップ - ベッティング] H --> I[ターン - コミュニティ4枚目公開] I --> J[ターン - ベッティング] J --> K[リバー - コミュニティ5枚目公開] K --> M[リバー - ベッティング] M --> N[ショーダウン - ハンド比較] N --> L L --> O{ゲーム続行?} O -->|はい| C O -->|いいえ| P[ゲーム終了 - 結果表示] P -->|リセットボタン| B ``` ## 画面の操作方法 ### ベッティング 1. **「コール」** ボタン: 相手のベットに合わせる 2. **「レイズ」** ボタン: ベット額を上げる(スライダーで金額指定) 3. **「ベット」** ボタン: 最初にベットする 4. **「チェック」** ボタン: パスする(ベットなしで次へ) 5. **「フォールド」** ボタン: 降りる 6. **「オールイン」** ボタン: 全チップを賭ける ### ショーダウン 1. **「ショー」** ボタン: 手札を公開する 2. **「マック」** ボタン: 手札を伏せる ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `C` | コール | ベッティング中 | | `R` | レイズ / ベット | ベッティング中 | | `K` | チェック | ベッティング中 | | `F` | フォールド | ベッティング中 | | `A` | オールイン | ベッティング中 | ### 学習モード 画面下部のトグルで学習モードを有効にすると、エクイティ(勝率)とポットオッズが表示されます。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ ベッティングリミット: [Pot Limit▼] │ │ トーナメントモード: [OFF▼] │ │ テーブルサイズ: [4-max▼] │ │ SB/BB: [5] / [10] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 980chips │ │ 1200chips│ │ 820chips │ │ │ │ TAG │ │ LAP │ │ TAP │ │ │ │ HUD stats│ │ HUD stats│ │ HUD stats│ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ コミュニティカード & ポット │ │ [♠A] [♥K] [♣9] [??] [??] ポット: 120 │ │ フェーズ: フロップ │ ├────────────────────────────────────────────┤ │ フッター: あなた (1500chips) [D] │ │ [♠Q] [♥J] [♣10] [♦8] │ │ HUD: VPIP 45% / PFR 20% / 3Bet 8% │ │ [フォールド][チェック][コール][レイズ ▼] │ │ [リセット] 学習モード: [ON/OFF] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - ベッティングリミット、トーナメントモード、テーブルサイズ、ブラインド額を設定 - `cpuMetaAI`: メタAI(CPUがプレイスタイルを学習) - `humanPlayMs`: アクション実行時の迷い時間(自動計測) - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU のチップ、プレイスタイル、HUD統計を表示 - **コミュニティカード & ポット**: 公開されたコミュニティカードとポット額 - **フッター(あなたの手札)**: - ホールカード4枚、チップ、HUD統計を表示 - ベッティングボタンで操作 ## 遊び方のコツ - テキサスホールデムと異なり、**必ずホールカード2枚を使う**ルールに注意しましょう - ダブルスーテッド(2組のスートペア)のハンドは強力です - コネクター(連続した数字)を多く含むハンドはストレートの可能性が高まります - ナッツ(最強ハンド)を狙える状況でのみ大きくベットするのが安全です - ポットリミットのため、テキサスホールデムよりもポットコントロールが重要です # ぶたのしっぽ(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶマッチング系カードゲームです。52枚のカードを裏向きに円形に並べ、順番に1枚ずつめくって中央の場に出します。出したカードのスートが場の一番上と一致すると、場のカードを全て引き取るペナルティを受けます。円形の山札がなくなった時点で最も多くカードを持っているプレイヤーの負けです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「Pig's Tail」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - カードを裏向きに円形の山札として配置 - プレイヤーが順番に山札から1枚引き、中央の場札の上に表向きで置く - 出したカードのスートが場札の一番上のカードと同じスートの場合、場札を全て引き取る(ペナルティ) - 場札が空の場合はペナルティなし ### ゲーム終了 - 円形の山札が全てなくなったらゲーム終了 - 手札が最も多いプレイヤーの負け、最も少ないプレイヤーの勝ち ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カードを円形に配置] C --> D{プレイヤーの手番?} D -->|はい| E[引くボタンをクリック] D -->|いいえ| F[CPU行動をアニメーション再生] F --> D E --> G{スートが場札トップと一致?} G -->|はい| H[場札を全て引き取る - ペナルティ] G -->|いいえ| I[カードを場に置く] H --> J{山札が空?} I --> J J -->|はい| K[結果表示] J -->|いいえ| F K -->|リセットボタン| B ``` ## 画面の操作方法 ### プレイフェーズ プレイヤーの手番では「引く」ボタンをクリックして山札から1枚カードを引きます。 ### 結果表示 - スートが一致した場合、ペナルティの演出が表示されます - ゲーム終了時、各プレイヤーの手札枚数と勝敗が表示されます ### CPUの手番 CPUの行動は自動で進行し、各アクションがアニメーション再生されます。 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 2枚 │ │ 4枚 │ │ 1枚 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ 山札: 40枚 場札: [♥7] │ ├────────────────────────────────────────────┤ │ [引く] [リセット] [設定] │ ├────────────────────────────────────────────┤ │ プレイヤー: 3枚 │ └────────────────────────────────────────────┘ ``` - **CPUプレイヤーエリア**: 各CPUの手札(ペナルティカード)枚数を表示 - **山札**: 円形の山札の残り枚数を表示 - **場札**: 中央の場札のトップカードを表示 - **引くボタン**: プレイヤーの手番で山札から1枚引く - **プレイヤーエリア**: プレイヤーの手札枚数を表示 ## 遊び方のコツ - このゲームは完全な運ゲームです。戦略的な要素はありません - 場札のスートとペナルティの演出を楽しみましょう - アクションログで過去のアクションを振り返ることができます # パイナップルポーカー(Web版)遊び方 ## ゲーム概要 パイナップルポーカー(Crazy Pineapple)は、テキサスホールデムの派生ゲームです。各プレイヤーに3枚のホールカードが配られ、フロップ後のベッティングラウンド終了後に1枚をディスカードします。残り2枚とコミュニティカード5枚から最強の5枚で役を作ります。テーブルサイズは4-max(デフォルト)、6-max、9-maxから選択可能です。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「パイナップルポーカー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用 - 各プレイヤーに3枚のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - フロップ後のベッティング終了後、各プレイヤーは1枚をディスカード(捨てる) - 残り2枚のホールカード + コミュニティカード5枚の計7枚から、最強の5枚の組み合わせで役を作る ### 役一覧(強い順) Royal Flush > Straight Flush > Four of a Kind > Full House > Flush > Straight > Three of a Kind > Two Pair > One Pair > High Card ### CPUプレイスタイル TAG(タイトアグレッシブ)、LAP(ルースパッシブ)、TAP(タイトパッシブ)、LAG(ルースアグレッシブ)、GTO(ゲーム理論最適)の5種類。 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[ブラインド投入・ホールカード3枚配布] C --> D[プリフロップ - ベッティング] D --> E[フロップ - コミュニティ3枚公開] E --> F[フロップベッティング] F --> G[ディスカードフェーズ - カード選択して捨てる] G --> H[ターン - 4枚目公開] H --> I[ベッティング] I --> J[リバー - 5枚目公開] J --> K[ベッティング] K --> L[ショーダウン - 勝者決定] L --> M{リバイ/アドオン?} M -->|はい| N[リバイ/アドオン選択] M -->|いいえ| O[次のハンドへ] N --> B O --> B ``` ## 画面の操作方法 ### プリフロップ・フロップ・ターン・リバーフェーズ ベッティングアクションボタンが表示されます: - **フォールド**: 手札を捨てて降りる - **チェック**: パスする(未ベット時のみ) - **コール**: 現在のベット額に合わせる - **ベット**: 金額を入力してベットする - **レイズ**: 金額を入力してレイズする - **オールイン**: 全チップを賭ける ### ディスカードフェーズ フロップベッティング終了後、手札の3枚から捨てるカードを選びます: 1. 手札の中から捨てたい1枚をクリックして選択 2. 「ディスカード」ボタンをクリックして確定 CPUは自動的にディスカードを行います。 ### ショーダウン 勝者が決定され、チップが配分されます。負けた場合はマック(手札を伏せる)またはショー(公開)を選択できます。 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `f` | フォールド | ベッティングフェーズ | | `c` | コール | ベッティングフェーズ | | `k` | チェック | ベッティングフェーズ(未ベット時) | ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ナビゲーションバー (ゲーム選択・言語切替) │ ├────────────────────────────────────────────┤ │ ゲーム情報 (ブラインド・ハンド数・ポット) │ ├────────────────────────────────────────────┤ │ CPUプレイヤー表示エリア │ │ (チップ・ベット額・HUD・カード枚数) │ ├────────────────────────────────────────────┤ │ コミュニティカード (最大5枚) │ ├────────────────────────────────────────────┤ │ プレイヤー手札 (3枚→ディスカード後2枚) │ │ チップ・ベット額・HUD統計 │ ├────────────────────────────────────────────┤ │ アクションボタン / ディスカードボタン │ │ (フェーズに応じて表示が切り替わる) │ ├────────────────────────────────────────────┤ │ メッセージ・CPU行動ログ │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ - 3枚のホールカードがあるため、プリフロップではホールデムより強い手が揃いやすいです - ディスカード時は、フロップとの相性が最も悪い1枚を捨てましょう - フラッシュドロー・ストレートドローが完成しやすい1枚を残すのが基本戦略です - ディスカード後はホールデムと同じ2枚なので、ターン以降の戦略はホールデムと共通です - 相手も3枚から良い2枚を選んでいるため、平均的なハンド強度はホールデムより高くなります # ピノクル(Web版)遊び方 ## ゲーム概要 4人2チーム(チーム0+2 vs チーム1+3)のトリックテイキング+メルドゲーム。48枚の専用デッキ(各スート9,10,J,Q,K,Aが2枚ずつ)を使用。ビッド、メルド(役の宣言)、トリックの3つのフェーズで構成され、先にポイント上限(デフォルト1500)に到達したチームが勝利。 ## ルール ### デッキ 各スート(スペード、クラブ、ハート、ダイヤ)の9, 10, J, Q, K, Aが2枚ずつ、計48枚。 ### カードランク A > 10 > K > Q > J > 9(10がKより強い) ### カードポイント A=11, 10=10, K=4, Q=3, J=2, 9=0。最終トリックボーナス=10点。 ### ビッドフェーズ ディーラーの左隣から順にビッド。最低ビッド20。パスするか、現在の最高ビッドより高くビッド。最高ビッダーがトランプスートを宣言。 ### メルドフェーズ 全プレイヤーが手札のメルド(役)を公開。「メルド確認」ボタンでプレイフェーズへ進む。 ### メルド一覧 | メルド | ポイント | 構成 | |--------|----------|------| | ディクス | 10 | トランプの9 | | コモンマリッジ | 20 | 非トランプのK-Q | | ロイヤルマリッジ | 40 | トランプのK-Q | | ピノクル | 40 | J♦ + Q♠ | | ジャックアラウンド | 40 | 各スートのJ | | クイーンアラウンド | 60 | 各スートのQ | | キングアラウンド | 80 | 各スートのK | | エースアラウンド | 100 | 各スートのA | | ラン | 150 | トランプのA-10-K-Q-J | | ダブルピノクル | 300 | 2組のJ♦ + Q♠ | | ダブルラン | 1500 | トランプのA-10-K-Q-J 2組 | ### トリックフェーズ 12トリック。リードスートに従う義務、リードスートがなければトランプを出す義務、可能なら勝てるカードを出す義務がある。プレイ可能なカードのみ選択可能。 ### 得点計算 - ビッドチーム: メルド+トリックポイントの合計がビッド以上なら加算、未満ならビッド額を失う - ディフェンダーチーム: 常に得点を加算 - トリックを1つも取れなかったチームのメルドは没収 ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始] --> B[ビッドフェーズ] B --> C[トランプ宣言] C --> D[メルドフェーズ] D --> E[トリックプレイ x12] E --> F[ラウンド得点計算] F --> G{ポイント上限到達?} G -->|はい| H[ゲーム終了] G -->|いいえ| B ``` ## 画面操作 ### ビッドフェーズ - 数値入力欄にビッド額を入力して「ビッド」ボタンをクリック - 「パス」ボタンでビッドをスキップ ### トランプ宣言フェーズ - スートボタン(♠ ♣ ♥ ♦)をクリックしてトランプスートを宣言 ### メルドフェーズ - 各プレイヤーのメルドが表示される - 「メルド確認」ボタンでプレイフェーズへ進む ### プレイフェーズ - プレイ可能なカードが強調表示される - カードをクリックしてプレイ ### 設定 - CPU難易度: Easy / Normal / Hard - ポイント上限: 500 / 1000 / 1500 / 2000 / 3000 # ポーカー(5カードドロー・Web版)遊び方 ## ゲーム概要 プレイヤーと1〜3人のCPU(設定可能)がテーブルで対戦する5カードドローポーカーです。4種のプレイスタイル(Conservative / Balanced / Aggressive / Bluffer)を持つCPU AI、オプションのジョーカーワイルドカード(0〜2枚)に対応しています。2回のベッティングラウンドと1回のカード交換を経て、最も強い役を持つプレイヤーが勝利します。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「Poker」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 役の強さ(弱い順) | 順位 | 役名 | 説明 | |------|------|------| | 0 | ハイカード | 役なし | | 1 | ワンペア | 同じ数字2枚 | | 2 | ツーペア | ペアが2組 | | 3 | スリーカード | 同じ数字3枚 | | 4 | ストレート | 連続する5枚(A-2-3-4-5 や 10-J-Q-K-A を含む) | | 5 | フラッシュ | 同じスート5枚 | | 6 | フルハウス | スリーカード + ワンペア | | 7 | フォーカード | 同じ数字4枚 | | 8 | ストレートフラッシュ | 同スート連続5枚 | | 9 | ロイヤルフラッシュ | 10-J-Q-K-A の同スート | | 10 | ファイブカード | 同じ数字5枚(ジョーカー使用時のみ成立) | ### チップシステム - 初期チップ: 1000 - アンティ(参加費): 10(各ラウンド開始時に全プレイヤーから自動徴収) - 最低ベット: 10 ### マルチプレイヤー - プレイヤー1人 + CPU 1〜3人(Web版ではリセット時に設定可能) - ディーラーはラウンドごとに時計回りで交代します - アクション順はディーラーの左隣から時計回りです ### ジョーカー - Web版ではリセット時に0〜2枚を設定可能 - ジョーカーはワイルドカードとして任意のカードの代わりになります - ジョーカーが含まれる場合のみ、ファイブカード(同じ数字5枚)が成立します ### CPU プレイスタイル | スタイル | 特徴 | |----------|------| | Conservative | 保守的。強い手でのみベットし、ブラフ率が低い | | Balanced | バランス型。標準的なベット判断とブラフ率 | | Aggressive | 攻撃的。高額ベットとレイズを多用する | | Bluffer | ブラフ重視。弱い手でも積極的にベット・レイズする | CPU はプレイスタイルに基づいてベット額、レイズ額、フォールド判断、ブラフ頻度を自動決定します。交換フェーズでは、フラッシュドロー・ストレートドロー等を考慮した戦略的な交換を行います。 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンで開始] B --> C[アンティ徴収 + 5枚配布] C --> D[第1ベッティングラウンド] D --> E{各プレイヤーが順番にアクション} E -->|CPUの番| F[CPU自動アクション] E -->|人間の番| G{ベットあり?} G -->|はい| H[コール / レイズ / フォールド / オールイン] G -->|いいえ| I[ベット / チェック / フォールド / オールイン] F --> J[次のプレイヤーへ] H --> J I --> J J -->|全員アクション完了| K[カード交換フェーズ] J -->|残り1人| L[最後の1人が勝利] K --> M{各プレイヤーが順番に交換} M -->|CPUの番| N[CPU自動交換] M -->|人間の番| O{カードをクリックして選択} O -->|交換ボタン| P[選択カードを交換] O -->|スタンドボタン| Q[交換なし] N --> R[次のプレイヤーへ] P --> R Q --> R R -->|全員交換完了| S[第2ベッティングラウンド] S --> T[第1ラウンドと同様のアクション] T -->|全員アクション完了| U[ショーダウン - 結果表示] T -->|残り1人| L L --> U U -->|リセットボタン| B ``` ## 画面の操作方法 ### 情報バー(画面上部) 画面上部の暗色バーに以下の情報が表示されます。 - **ポット**: 場に出ているチップ合計 - **ディーラー**: 現在のディーラー位置(Player N) - **ジョーカー**: ジョーカー枚数(1枚以上の場合のみ表示) - **Lowball**: 2-7 ローボールモードが有効な場合にインジケーターが表示されます ### CPUプレイヤーエリア(スクロール領域) 各CPUプレイヤーの情報が表示されます。 - **CPU N (スタイル名)**: CPUプレイヤー番号とプレイスタイル名(Conservative / Balanced / Aggressive / Bluffer) - **チップ**: 所持チップ数 - **ベット**: 現ラウンドのベット額(ベット時のみ表示) - **[フォールド]**: フォールド済みのプレイヤー(赤色バッジ) - **[オールイン]**: オールイン済みのプレイヤー(黄色バッジ) - **交換: N枚**: カード交換枚数(第2ベッティングラウンド以降、フォールドしていない場合に表示) - **役名バッジ**: ショーダウン時にフォールドしていないプレイヤーの役名を表示(黄色背景) - **カード**: ショーダウンまでは裏向き(CardBack)、END フェーズでフォールドしていない場合は表向き ### CPU行動ログ CPUが実行したベッティングアクションの記録が暗色パネルに表示されます。 ``` CPU行動: Player 1: ベット (10) Player 2: コール (10) Player 3: フォールド ``` ### CPU交換ログ CPUのカード交換枚数の記録が暗色パネルに表示されます。 ``` CPU交換: Player 1: 2枚交換 Player 2: 3枚交換 ``` ### 結果表示 ショーダウン(END フェーズ)時に暗色パネルで各プレイヤーの結果を表示します。 ``` 結果: あなた: Two Pair +60チップ CPU 1: One Pair CPU 2: High Card ``` ### プレイヤーエリア(固定フッター) 画面下部の固定エリアに人間プレイヤーの情報とコントロールが表示されます。 #### 手札表示 - 人間プレイヤーの手札は常に表向きで表示されます - チップ数、ベット額、状態バッジ(フォールド / オールイン)が表示されます - ショーダウン時には役名バッジも表示されます #### メッセージバー 勝敗メッセージやエラーメッセージが中央に表示されます。 #### 第1ベッティングラウンド / 第2ベッティングラウンド 自分の番で、かつフォールド・オールインしていない場合にベッティングコントロールが表示されます。 **ベット済みの場合(他プレイヤーのベットがある場合):** - **「コール」** ボタン: 直前のベットと同額を支払う - **「レイズ」** ボタン + 金額入力: 上乗せ額を入力してレイズ - **「フォールド」** ボタン: 降りる - **「オールイン」** ボタン: 全チップを賭ける **未ベットの場合:** - **「ベット」** ボタン + 金額入力: ベット額を入力 - **「チェック」** ボタン: ベットせずに次へ進む - **「フォールド」** ボタン: 降りる - **「オールイン」** ボタン: 全チップを賭ける 金額入力フィールドには最低レイズ額がデフォルト値として表示されます。 #### カード交換フェーズ(EXCHANGE フェーズ) 自分の番でカード交換操作が可能になります。 1. 交換したいカードをクリックして選択します(黄色の枠で強調され、カードが持ち上がり「交換」ラベルが表示されます) 2. 再度クリックで選択解除できます 3. カードを選択すると、**ドローオッズパネル**が自動表示され、交換後に各役が成立する確率をリアルタイムで確認できます 4. **「交換」** ボタンをクリックして選択したカードを交換 5. 交換しない場合は **「スタンド」** ボタンをクリック ガイドメッセージ「交換したいカードをクリックして選択し、「交換」または「スタンド」を押してください。」が表示されます。 #### ドローオッズパネル カード交換フェーズで1枚以上のカードを選択すると、「ドローオッズ」パネルが交換ボタンの上に表示されます。各役名と成立確率(%)がリアルタイムで更新されます。確率が0%の役は非表示になります。 ``` ドローオッズ: One Pair 42.3% Two Pair 10.5% Flush 4.2% ``` - 選択カードを変更するたびに300msのデバウンス後にサーバーへ問い合わせます - 全カードの選択を解除するとパネルは非表示になります - 交換またはスタンド実行後にパネルは自動的にクリアされます #### 結果フェーズ(END) - 全CPUプレイヤーの手札が表向きになります(フォールドしたCPUは裏向きのまま) - 各プレイヤーの役名がバッジで表示されます(例: 「One Pair」「Flush」「Five of a Kind」) - 勝敗メッセージが表示されます - **「リセット」** ボタンで次のゲームへ #### ローボールモード リセットボタンの横にある **「Lowball」チェックボックス** で 2-7 ローボールモードを切り替えられます。有効にすると最も弱い手が勝利するルールになります(Ace は常に 14、ストレートとフラッシュはカウントされ不利、最強の手は 2-3-4-5-7 オフスート)。モードが有効な場合、情報バーにインジケーターが表示されます。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | 交換フェーズ | | `Enter` | 交換実行 | 交換フェーズ | | `Escape` | 選択クリア | 交換フェーズ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌─────────────────────────────────────────────────────┐ │ ポット: 40 | ディーラー: Player 0 | ジョーカー: 2 │ ├─────────────────────────────────────────────────────┤ │ CPU 1 (Balanced) チップ:980 ベット:10 │ │ [裏] [裏] [裏] [裏] [裏] │ │ │ │ CPU 2 (Aggressive) チップ:970 ベット:20 │ │ [裏] [裏] [裏] [裏] [裏] │ │ │ │ CPU 3 (Bluffer) チップ:990 [フォールド] │ │ [裏] [裏] [裏] [裏] [裏] │ │ │ │ ┌───────────────────────────────────┐ │ │ │ CPU行動: │ │ │ │ Player 1: ベット (10) │ │ │ │ Player 2: レイズ (20) │ │ │ │ Player 3: フォールド │ │ │ └───────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────┐ │ │ │ CPU交換: │ │ │ │ Player 1: 2枚交換 │ │ │ │ Player 2: 3枚交換 │ │ │ └───────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────┐ │ │ │ 結果: │ (ENDのみ) │ │ │ あなた: Two Pair +60チップ │ │ │ │ CPU 1: One Pair │ │ │ │ CPU 2: High Card │ │ │ └───────────────────────────────────┘ │ ├─────────────────────────────────────────────────────┤ │ あなたの手札 チップ:960 ベット:20 │ │ [♠3] [♥7] [♦2] [♣9] [♥K] │ │ ↑交換 │ │ │ │ ┌────────────────────────────────┐ │ │ │ You are the winner. │ │ │ └────────────────────────────────┘ │ │ │ │ ベット額: [___] │ │ [コール] [レイズ] [フォールド] [オールイン] │ │ ┌──────────────────────────────┐ │ │ │ ドローオッズ: │ (交換フェーズのみ) │ │ │ One Pair 42.3% │ │ │ │ Two Pair 10.5% │ │ │ └──────────────────────────────┘ │ │ [交換] [スタンド] │ │ [リセット] │ └─────────────────────────────────────────────────────┘ ``` - 上部: 情報バー(ポット、ディーラー、ジョーカー枚数) - 中央スクロール領域: CPUプレイヤーカード + CPU行動ログ + CPU交換ログ + 結果表示 - 下部固定フッター: 人間プレイヤーの手札 + メッセージ + 操作ボタン - CPUのカードはショーダウンまで裏向き(CardBack で表示) - プレイヤーのカードは常に表向き - 交換フェーズで選択中のカードは黄色枠 + 上方シフト + 「交換」ラベル - ベッティングコントロールは自分の番でのみ表示 - 交換コントロールは交換フェーズで自分の番のときのみ表示 ## Web API 設定 Web版ではリセットコマンド実行時にJSON リクエストで CPU 人数とジョーカー枚数を設定できます。 ```json { "command": "reset", "sessionId": "...", "cpuCount": 2, "jokerCount": 1, "isLowball": true } ``` | パラメータ | 型 | 範囲 | デフォルト | 説明 | |------------|------|------|------------|------| | `cpuCount` | int | 1〜3 | 3 | CPU プレイヤー人数 | | `jokerCount` | int | 0〜2 | 0 | ジョーカー枚数 | | `bettingLimit` | int | 0〜2 | 0 | ベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit) | | `isLowball` | bool | — | false | 2-7 ローボールモード(最弱の手が勝利) | | `cpuMetaAI` | bool | — | false | メタAI有効化(CPUが人間のブラフ率・フォールド率・思考時間を学習して戦略を適応) | | `humanPlayMs` | int | — | — | 人間のアクションにかかった時間(ミリ秒)。メタAI有効時に送信 | ### レスポンスのベッティングリミット関連フィールド | フィールド | 型 | 説明 | |------------|------|------| | `bettingLimit` | int | 現在のベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit) | | `raiseCount` | int | 現在のベッティングラウンドでのレイズ回数 | | `maxBetAmount` | int | 現在のベッティングリミットに基づく最大ベット額 | | `isLowball` | bool | 2-7 ローボールモードが有効かどうか | # ピラミッド(Web版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、7段のピラミッド型に並べたカードから、合計13になるペアを見つけて除去していきます。ピラミッドのカードをすべて除去すればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ピラミッド」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 7段のピラミッドに28枚を表向きで配る(段iにはi+1枚) - 残りの24枚は山札(ストック)に裏向きで置く - 「露出カード」(下の段の左右両方のカードが除去済み)のみ選択可能 - 最下段(7段目)のカードは常に露出状態 ### カードの値 | カード | 値 | |--------|-----| | A | 1 | | 2〜10 | 数字通り | | J | 11 | | Q | 12 | | K | 13 | ### 除去ルール - 露出カード2枚の合計が13になるペアを除去できる - K(値13)は単独で除去できる - 山札からウェイストに引いたカードとピラミッドの露出カードでペアを作れる - ウェイストのトップがKの場合、単独で除去できる ### ゲームクリア - ピラミッドの28枚すべてを除去するとゲームクリア ### ゲームオーバー - これ以上除去可能な手がない場合はゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札が空の場合に「手詰まりです」とメッセージが表示されます - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カードを配る] C --> D{除去可能な手がある?} D -->|はい| E{プレイヤーのアクション} E -->|山札クリック| F[山札からウェイストにカードを引く] E -->|カードをクリック| G[カードを選択・ペア除去] E -->|ヒントボタン| H[ヒントを表示] E -->|元に戻すボタン| I[直前の操作を取り消す] F --> D G --> J{ピラミッド全除去?} H --> D I --> D J -->|いいえ| D J -->|はい| K[ゲームクリア] D -->|いいえ| L[ゲームオーバー] K -->|リセットボタン| B L -->|リセットボタン| B ``` ## 画面の操作方法 ### カードを除去する 1. 露出カードをクリックして選択します(ハイライト表示) 2. もう1枚の露出カードをクリックしてペア除去します(合計13になる必要あり) 3. Kをクリックすると単独で除去されます 4. ウェイストのトップカードもクリックで選択でき、ピラミッドのカードとペアにできます ### 山札を引く 1. 山札エリアをクリックしてカードをウェイストに引きます 2. 山札が空になると引けなくなります ### アンドゥ(元に戻す) **「元に戻す」** ボタンまたは `z` キーで直前の操作を取り消せます。何度でも元に戻せます。 ### ヒント **「ヒント」** ボタンをクリックすると、次に可能な手を提案します。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `d` | カードを引く(ドロー) | プレイ中 | | `z` | 元に戻す(アンドゥ) | プレイ中 | | `h` | ヒント | プレイ中 | | `g` | ギブアップ | プレイ中 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ゲーム情報 │ │ 手数: 0 │ ├────────────────────────────────────────────┤ │ ピラミッドエリア │ │ [♠A] │ │ [♥5] [♦8] │ │ [♣3] [♠10] [♥2] │ │ [♦7] [♣6] [♠4] [♥9] │ │ [♦Q] [♣J] [♠2] [♥3] [♦K] │ │ [♣9] [♠7] [♥6] [♦4] [♣8] [♠5] │ │ [♥Q] [♦A] [♣10] [♠3] [♥7] [♦6] [♣4] │ ├────────────────────────────────────────────┤ │ 山札・ウェイストエリア │ │ [山札: 24枚] [ウェイスト: --] │ ├────────────────────────────────────────────┤ │ [引く] [元に戻す] [ヒント] │ │ [ギブアップ] [リセット] │ └────────────────────────────────────────────┘ ``` - **ピラミッドエリア**: 7段のピラミッド型にカードを表示 - 露出カード: クリックで選択可能(明るく表示) - 非露出カード: クリック不可(暗く表示) - 除去済み: 空白表示 - **山札エリア**: クリックでカードをウェイストに引く(残りカード数表示) - **ウェイストエリア**: 引いたカードが表示される - **ボタン**: - 「引く」: 山札からカードを引く - 「元に戻す」: 直前の操作を取り消す - 「ヒント」: 次の一手を提案 - 「ギブアップ」: 現在のゲームを終了 - 「リセット」: 新しいゲームを開始 ## 遊び方のコツ - まずピラミッドの下段から除去できるペアを探しましょう - Kが見えたらすぐにクリックして単独除去しましょう - 上段のカードを露出させるために、下段のカードを優先的に除去します - 山札を引く前に、ピラミッド内で除去できるペアがないか確認しましょう - ヒントボタンで詰まった時のアドバイスを得られます - アンドゥを活用して、異なる戦略を試しましょう # セブンカード・スタッド(Web版)遊び方 ## ゲーム概要 セブンカード・スタッドポーカーです。コミュニティカードを使わない古典的なスタッドポーカーで、各プレイヤーに7枚のカードが配られ、最強の5枚の組み合わせで役を作ります。テーブルサイズは2人~7人(デフォルト4人 = 1人+CPU 3人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%/3Bet%/AF)が全プレイヤーに表示されます。トーナメントモードでは、指定ハンド数ごとにアンティが自動的に上昇します。リバイ(チップ再購入)とアドオン(一回限りのチップ追加購入)にも対応しています。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「セブンカード・スタッド」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用 - コミュニティカードなし — 各プレイヤーに個別にカードが配られる - 合計7枚(裏向き3枚 + 表向き4枚)から最強の5枚の組み合わせで役を作る - ブラインドの代わりにアンティ(全員が支払う参加費)とブリングイン(最も低いドアカードのプレイヤーが支払う強制ベット)を使用 - 各ストリートでベッティング順は表向きカードの最強ハンドから開始 ### ゲームの進行 1. **アンティ**: 全プレイヤーがアンティ(デフォルト2)を支払う 2. **サードストリート**: 裏向き2枚(ホールカード)+ 表向き1枚(ドアカード)配布。最も低いドアカードのプレイヤーがブリングイン(デフォルト5)を投入。ベッティングラウンド(スモールベット) 3. **フォースストリート**: 表向き1枚追加(計4枚)。最強の表向きハンドから開始。ベッティングラウンド(スモールベット) 4. **フィフスストリート**: 表向き1枚追加(計5枚)。最強の表向きハンドから開始。ベッティングラウンド(ビッグベット) 5. **シックスストリート**: 表向き1枚追加(計6枚)。最強の表向きハンドから開始。ベッティングラウンド(ビッグベット) 6. **セブンスストリート**: 裏向き1枚追加(計7枚)。最強の表向きハンドから開始。最後のベッティングラウンド(ビッグベット) 7. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 8. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 9. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | Full House | 同じ数字3枚 + 同じ数字2枚 | | Flush | 同じスート5枚 | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略 | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[アンティ投入・カード配布] C --> D[CPUが自動でアクション] D --> E{プレイヤーの手番?} E -->|はい| F[アクションボタンで操作] E -->|いいえ| G[CPUが自動でアクション] G --> H{ストリート進行?} F --> H H -->|サード→フォース| I[4枚目配布] H -->|フォース→フィフス| J[5枚目配布] H -->|フィフス→シックス| K[6枚目配布] H -->|シックス→セブンス| L[7枚目配布] H -->|セブンス→ショーダウン| M[全員の手札公開] I --> D J --> D K --> D L --> D M --> N[勝者決定・結果表示] N --> N2{プレイヤーが負けた?} N2 -->|はい| N3[マック/ショー選択] N3 --> O2{リバイ/アドオン対象?} N2 -->|いいえ| O2 O2 -->|はい| P2[リバイ/アドオン選択画面] P2 -->|リセットボタン| B O2 -->|いいえ| Q2[次のハンドへ] Q2 -->|リセットボタン| B ``` ## 画面の操作方法 ### アクションの実行 自分の手番が来ると、画面下部にアクションボタンが表示されます。 **ベットが出ていない場合:** - **「ベット」**: ベット額を入力してベットする - **「チェック」**: パスする **ベットが出ている場合:** - **「コール」**: 現在のベット額に合わせる - **「レイズ」**: ベット額を入力してレイズする **常に選択可能:** - **「フォールド」**: 降りる - **「オールイン」**: 全チップを賭ける ### ベット額の入力 アクションボタンの上にある「ベット額」入力欄で金額を指定します。ベットまたはレイズ時にこの金額が使用されます。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `c` | コール | アクションフェーズ(ベットがある場合) | | `r` | レイズ / ベット | アクションフェーズ | | `k` | チェック | アクションフェーズ(ベットがない場合) | | `f` | フォールド | アクションフェーズ | | `a` | オールイン | アクションフェーズ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ フェーズ: フォースストリート ポット: 19 │ │ アンティ: 2 ブリングイン: 5 SB/BB: 10/20 │ │ ディーラー: Player 0 │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ CPU 1 (TAG) チップ:988 ベット:5 │ │ ホール: [🂠] [🂠] ドア: [♦7] [♠4] │ │ CPU 2 (LAP) チップ:998 [フォールド] │ │ ホール: [🂠] [🂠] ドア: [♠3] [♥9] │ │ CPU 3 (GTO) チップ:988 ベット:5 │ │ ホール: [🂠] [🂠] ドア: [♥J] [♦Q] │ ├────────────────────────────────────────────┤ │ CPU行動ログ │ │ Player 1: コール │ │ Player 2: フォールド │ │ Player 3: コール │ ├────────────────────────────────────────────┤ │ フッター: あなたの手札 │ │ チップ:988 ベット:5 │ │ ホール: [♠A] [♥K] ドア: [♣10] [♦J] │ │ │ │ メッセージエリア │ │ │ │ ベット額: [10 ▲▼] │ │ [コール] [レイズ] [フォールド] [オールイン] │ │ [リセット] │ └────────────────────────────────────────────┘ ``` - **情報バー(上部)**: 現在のフェーズ、ポット額、アンティ/ブリングイン/SB/BB額、ディーラー位置(トーナメントモード時はハンド数とレベルアップ情報も表示) - **CPUプレイヤーエリア**: 各CPUのプレイスタイル、チップ、ベット額、状態 - ホールカードは裏向き、ドアカードは表向きで表示 - ショーダウン時にはCPUのホールカードと役名が表示される - **CPU行動ログ**: CPUが行ったアクションの記録 - **結果エリア**: ショーダウン後、各プレイヤーの役と獲得チップを表示 - **フッター(あなたの手札)**: - ホールカード(裏向き、自分だけ見える)とドアカード(表向き)が表示される - フォールド/オールイン状態のバッジ - ショーダウン時には役名が表示される - **メッセージエリア**: ゲームの状態メッセージ - **アクションボタン**: 手番時のみ表示 ## HUD統計 各プレイヤーにはHUD(ヘッズアップディスプレイ)統計が表示されます: - **VPIP** (Voluntarily Put $ In Pot): サードストリートで自発的にチップを入れた割合 - **PFR** (Pre-Flop Raise): サードストリートでレイズした割合 - **3Bet**: 3ベット率 - **AF** (Aggression Factor): アグレッションファクター 統計はハンド数が1以上のプレイヤーに対してのみ表示されます。 ## トーナメントモード トーナメントモードを有効にすると、指定ハンド数ごとにアンティが自動的に上昇します。情報バーにハンド数とレベルアップ情報が追加表示されます。 API経由で以下のパラメータを指定できます: - **tournamentMode**: `true` でトーナメントモードを有効化 - **anteLevelHands**: アンティレベルアップまでのハンド数(デフォルト10) - **anteMultiplier**: アンティ倍率(百分率、デフォルト200 = 2倍) - **bettingLimit**: ベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit、デフォルト0) - **tableSize**: テーブルサイズ(2~7、デフォルト4) - **rebuyEnabled**: `true` でリバイを有効化(デフォルト`false`) - **rebuyMaxCount**: リバイの最大回数(デフォルト3) - **rebuyChips**: リバイで得られるチップ数(デフォルト1000) - **rebuyPeriodHands**: リバイ可能なハンド数(デフォルト10) - **addonEnabled**: `true` でアドオンを有効化(デフォルト`false`) - **addonChips**: アドオンで得られるチップ数(デフォルト1500) - **addonAfterHand**: アドオン可能になるハンド番号(デフォルト10) - **cpuMetaAI**: `true` でメタAIを有効化(デフォルト`false`) - **humanPlayMs**: 人間のアクションにかかった時間(ミリ秒) ## マック/ショー ショーダウンでプレイヤーが負けた場合、手札の公開/非公開を選択できます。 - **「マック」ボタン**: 手札を伏せて非公開にする - **「ショー」ボタン**: 手札を公開する マックした場合、結果表示で手札が非表示になります。 ## リバイ/アドオン ### リバイ リバイ期間内にチップが0になった場合���リバイ画面が表示されます。 - **「リバイ」ボタン**: チップを再購入して続行 - **「スキップ」ボタン**: リバイせずに続行 ### アドオン 指定ハンド数経過後にアドオン画面が表示されます(1回のみ)。 - **「アドオン」ボタン**: チップを追加購入 - **「スキップ」ボタン**: アドオンせずに続行 CPUプレイヤーはリバイ・アドオンを自動的に実行します。 ## 遊び方のコツ - サードストリートで強いドアカード(A/K/Q)を見せると、弱い手を持つ対戦相手をフォールドさせやすくなります - 他プレイヤーの表向きカードをよく観察し、自分のアウツ(必要なカード)が場に出ていないか確認しましょう - フィフスストリート以降はビッグベットになるため、弱い手で深追いしないようにしましょう - ペアが見えているプレイヤーがレイズしてきたら、スリーカード(トリップス)の可能性を考慮しましょう - HUD統計を活用して、CPUのプレイスタイルの傾向を把握しましょう - トーナメントモードではアンティが上がるため、早めにチップを稼ぐ戦略が重要です - 表向きカードの情報は非常に重要です。相手のフラッシュやストレートの可能性を常に確認しましょう # 7並べ(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶカードゲームです。場の7を起点に、隣接する数字のカードを順番に並べていきます。手札を最初になくしたプレイヤーが1位です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「Sevens」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを使用(オプションでジョーカー0〜2枚追加) - 各スートの7が最初に場に置かれ、プレイヤーの手札から除去される - 場に出ているカードの隣接値(±1)のカードのみ出せる - カードが出せない場合はパス(デフォルト最大5回、設定で変更可能、0で無制限) - パスを使い切り、出せるカードもない場合は失格(残りの手札が場に強制配置される) - 最初に手札をなくしたプレイヤーが1位 ### オプションルール 画面下部の設定パネルで設定を変更できます(リセット時に反映)。 | オプション | 説明 | |------------|------| | トンネル | AとKが循環して繋がる(A↔K) | | トンネルスキップ | 通常の±1に加え、±N離れたカードからも繋げられる(2〜6、トンネル有効時は循環ラップ) | | ジョーカー | ジョーカーを場の任意の有効位置に配置できる(0〜2枚) | | CPU戦略 | CPUの思考モード。なし/戦略的/嫌がらせ特化の3段階から選択 | | パス回数 | 最大パス回数を変更(デフォルト5回、0で無制限) | | ジョーカー上がり禁止 | 最後のカードがジョーカーの場合、出せない(パスまたは失格) | | ジョーカー回収 | ジョーカーが配置された位置に本物のカードを出すと、ジョーカーが手札に戻る | | 片側ストップ | Aを置くと上側(8-K)がブロック、Kを置くと下側(A-6)がブロックされる | | ジョーカー連続禁止 | 前の手番でジョーカーを出したプレイヤーは、次の手番でジョーカーを出せない(通常カードを出すかパスするとリセット) | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[設定を選択してリセット] B --> C[7を場に配置 + カード配布] C --> D[CPUが自動で手番を進行] D --> E{プレイヤーの手番?} E -->|はい| F{出せるカードがある?} E -->|いいえ| G[CPUが自動でプレイ] G --> E F -->|はい| H[カードをクリックして出す] F -->|いいえ| I[パスボタン] H --> J{ジョーカー?} J -->|はい| K[ボード上の配置先をクリック] J -->|いいえ| L[カードが場に配置される] I --> L K --> L L --> M{ゲーム終了?} M -->|いいえ| D M -->|はい| N[最終順位表示] N -->|リセットボタン| B ``` ## 画面の操作方法 ### ゲーム設定(画面下部の設定パネル) | 設定 | UI | |------|----| | トンネル | チェックボックス | | トンネルスキップ幅 | ドロップダウン(なし / 2 / 3 / 4 / 5 / 6) | | CPU戦略 | ドロップダウン(なし / 戦略的 / 嫌がらせ特化) | | ジョーカー | ドロップダウン(0 / 1 / 2) | | パス回数 | ドロップダウン(3 / 5 / 10 / 無制限) | | ジョーカー上がり禁止 | チェックボックス | | ジョーカー回収 | チェックボックス | | 片側ストップ | チェックボックス | | ジョーカー連続禁止 | チェックボックス | 設定変更後 **「リセット」** ボタンをクリックすると新しい設定でゲームが開始されます。 ### カードの出し方 1. 手札の中で出せるカードは緑色の枠で表示されます 2. 出せるカードをクリックすると場に配置されます 3. 出せないカードは薄く(半透明)表示され、クリックできません ### ジョーカーの使い方 1. 手札のジョーカーをクリックします 2. ボード上の配置可能な位置が青色に変わります 3. 青色のマスをクリックしてジョーカーを配置します 4. **「キャンセル」** ボタンで選択をやめることもできます ### パス - **「パス」** ボタンをクリックしてパスします - パス残回数が0の場合、ボタンは無効化されます ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カードを直接出す(1=1枚目, ..., 9=9枚目, 0=10枚目) | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │12枚 │ │13枚 │ │上がり │ │ │ │パス: 1/5 │ │パス: 0/5 │ │1位 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ボード (場札) │ │ ♠: [A][2][3][4][5][6][7][8][9][ ][ ][ ][ ]│ │ ♣: [ ][ ][ ][7][ ][ ][ ][ ][ ] │ │ ♥: [ ][ ][ ][7][8][9][10][ ][ ] │ │ ♦: [ ][ ][4][5][6][7][ ][ ][ ][ ][ ] │ │ (緑=配置済み, 橙=7, 青=ジョーカー配置可) │ ├────────────────────────────────────────────┤ │ [リセット] [パス] [キャンセル] │ ├────────────────────────────────────────────┤ │ 設定: ☐トンネル CPU戦略:[なし▼] ジョーカー:[0▼] パス:[5▼] ☐ジョーカー上がり禁止 ☐片側ストップ ☐ジョーカー連続禁止│ ├────────────────────────────────────────────┤ │ プレイヤーの手札 (パス: 0/5) │ │ [♠2][♠3][♥10][♣K][JOKER] ... │ │ (緑枠=出せる, 薄=出せない) │ └────────────────────────────────────────────┘ ``` - **ボード**: 4スート × 13値のグリッド表示 - 緑色: 配置済みのカード - 橙色/金色: 7(初期配置) - 青色: ジョーカー選択モード時の配置可能位置 - 暗い: 未配置 - トンネル有効時: KとAの間に視覚的な「↔」コネクタが表示され、反対端(KまたはA)が配置済みの場合は黄色枠でハイライトされる - **プレイヤーの手札**: 緑枠のカードが出せるカード、薄く表示されたカードは出せない - **CPU パネル**: 手番のCPUは金色枠 + 「考え中...」バッジ。CPUの行動は1件ずつアニメーションで順番に表示され(約800ms間隔)、アニメーション中はボタンが無効化される - **強制パス表示**: 出せるカードがない場合のパスは赤色背景 + `⚠ 出せるカードなし!` で強調表示 ## 遊び方のコツ - パスはデフォルト5回まで(設定で変更可能)なので、計画的に使いましょう - 出せるカード(緑枠)を確認してからプレイしましょう - ジョーカーは場の好きな有効位置に配置できるため、戦略的に使うと有利です - トンネルルールを有効にすると、AとKが循環するため選択肢が広がります # ショートデック / 6+ホールデム(Web版)遊び方 ## ゲーム概要 ショートデック(6+ホールデム)は、テキサスホールデムの変種で、36枚のデッキ(各スートの2〜5を除去)を使用します。テーブルサイズは4-max(デフォルト、1人+CPU 3人)、6-max(1人+CPU 5人)、9-max(1人+CPU 8人)から選択できます。各CPUは異なるプレイスタイル(TAG/LAP/TAP/LAG/GTO)を持ち、ブラフを含む戦略的なプレイをします。GTOスタイルはボードテクスチャ分析に基づく混合戦略で意思決定します。サイドポットにも対応しています。 HUD統計(VPIP%/PFR%)が全プレイヤーに表示され、CPUのベットサイズはポット相対(半額〜全額)で決定されます。トーナメントモードでは、指定ハンド数ごとにブラインドが自動的に上昇します。リバイ(チップ再購入)とアドオン(一回限りのチップ追加購入)にも対応しています。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ショートデック」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - **36枚**のトランプを使用(各スートの2・3・4・5を除去) - 各プレイヤーに2枚のホールカード(手札)が配られる - 場にコミュニティカード(共有カード)が最大5枚公開される - ホールカード2枚 + コミュニティカード5枚の計7枚から、最強の5枚の組み合わせで役を作る - 最も強い役を持つプレイヤーがポットを獲得する ### テキサスホールデムとの違い | 項目 | テキサスホールデム | ショートデック | |------|-------------------|---------------| | デッキ枚数 | 52枚 | 36枚(2〜5除去) | | フラッシュ vs フルハウス | フルハウス > フラッシュ | **フラッシュ > フルハウス** | | 最低ストレート | A-2-3-4-5 | **A-6-7-8-9** | ### ゲームの進行 1. **ブラインド**: ディーラーの左隣がスモールブラインド(デフォルト5)、その左隣がビッグブラインド(デフォルト10)を強制ベット 2. **プリフロップ**: ホールカード2枚配布後、最初のベッティングラウンド 3. **フロップ**: コミュニティカード3枚公開後、ベッティングラウンド 4. **ターン**: コミュニティカード4枚目公開後、ベッティングラウンド 5. **リバー**: コミュニティカード5枚目公開後、最後のベッティングラウンド 6. **ショーダウン**: 残っているプレイヤーの手札を公開し、勝者を決定 7. **マック/ショー**: ショーダウンで負けた場合、手札をマック(伏せる)またはショー(公開)を選択 8. **リバイ/アドオン**: リバイ・アドオンが有効な場合、該当条件を満たすとチップの再購入・追加購入が可能 ### ベッティングアクション | アクション | 説明 | |-----------|------| | フォールド | 手札を捨ててラウンドから降りる | | チェック | ベットせずにパスする(未ベット時のみ) | | コール | 現在のベット額に合わせる | | ベット | 新たにベットする(未ベット時のみ) | | レイズ | 現在のベット額を上げる | | オールイン | 全チップを賭ける | ### 役一覧(強い順) | 役 | 説明 | |----|------| | Royal Flush | 同じスートの10・J・Q・K・A | | Straight Flush | 同じスートの5枚連番 | | Four of a Kind | 同じ数字4枚 | | **Flush** | **同じスート5枚(通常のホールデムより上位)** | | **Full House** | **同じ数字3枚 + 同じ数字2枚(通常のホールデムより下位)** | | Straight | 5枚連番 | | Three of a Kind | 同じ数字3枚 | | Two Pair | ペア2組 | | One Pair | ペア1組 | | High Card | 上記に該当しない最も高いカード | ### CPUプレイスタイル | スタイル | 略称 | 特徴 | |---------|------|------| | Tight-Aggressive | TAG | 強い手のみ参加し、積極的にレイズ。ブラフ率15% | | Loose-Passive | LAP | 多くの手に参加するが、主にコール。ブラフ率5% | | Tight-Passive | TAP | 厳選した手のみ参加し、主にコール。ブラフ率5% | | Loose-Aggressive | LAG | 多くの手に参加し、頻繁にレイズ。ブラフ率30% | | Game Theory Optimal | GTO | ハンド強度とボードテクスチャに基づく確率的混合戦略。ドライボードでは2/3ポット、ウェットボードでは3/4ポットのベットサイズ | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[ブラインド投入・ホールカード配布] C --> D[CPUが自動でアクション] D --> E{プレイヤーの手番?} E -->|はい| F[アクションボタンで操作] E -->|いいえ| G[CPUが自動でアクション] G --> H{フェーズ進行?} F --> H H -->|プリフロップ→フロップ| I[コミュニティカード3枚公開] H -->|フロップ→ターン| J[4枚目公開] H -->|ターン→リバー| K[5枚目公開] H -->|リバー→ショーダウン| L[全員の手札公開] I --> D J --> D K --> D L --> M[勝者決定・結果表示] M --> M2{プレイヤーが負けた?} M2 -->|はい| M3[マック/ショー選択] M3 --> N2{リバイ/アドオン対象?} M2 -->|いいえ| N2{リバイ/アドオン対象?} N2 -->|はい| O2[リバイ/アドオン選択画面] O2 -->|リセットボタン| B N2 -->|いいえ| P2[次のハンドへ] P2 -->|リセットボタン| B ``` ## 画面の操作方法 ### アクションの実行 自分の手番が来ると、画面下部にアクションボタンが表示されます。 **ベットが出ていない場合:** - **「ベット」**: ベット額を入力してベットする - **「チェック」**: パスする **ベットが出ている場合:** - **「コール」**: 現在のベット額に合わせる - **「レイズ」**: ベット額を入力してレイズする **常に選択可能:** - **「フォールド」**: 降りる - **「オールイン」**: 全チップを賭ける ### ベット額の入力 アクションボタンの上にある「ベット額」入力欄で金額を指定します。ベットまたはレイズ時にこの金額が使用されます。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `c` | コール | アクションフェーズ(ベットがある場合) | | `r` | レイズ / ベット | アクションフェーズ | | `k` | チェック | アクションフェーズ(ベットがない場合) | | `f` | フォールド | アクションフェーズ | | `a` | オールイン | アクションフェーズ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ フェーズ: フロップ ポット: 60 SB/BB: 5/10 ディーラー: Player 0 │ ├────────────────────────────────────────────┤ │ コミュニティカード │ │ [♠7] [♣10] [♥9] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ CPU 1 (TAG) チップ:980 ベット:20 │ │ [🂠] [🂠] │ │ CPU 2 (LAP) チップ:990 [フォールド] │ │ [🂠] [🂠] │ │ CPU 3 (GTO) チップ:960 ベット:10 │ │ [🂠] [🂠] │ ├────────────────────────────────────────────┤ │ CPU行動ログ │ │ Player 1: コール │ │ Player 2: フォールド │ │ Player 3: チェック │ ├────────────────────────────────────────────┤ │ フッター: あなたの手札 │ │ チップ:970 ベット:20 │ │ [♠A] [♥K] │ │ │ │ メッセージエリア │ │ │ │ ベット額: [20 ▲▼] │ │ [コール] [レイズ] [フォールド] [オールイン] │ │ [リセット] │ └────────────────────────────────────────────┘ ``` - **情報バー(上部)**: 現在のフェーズ、ポット額、SB/BB額、ディーラー位置(トーナメントモード時はハンド数とレベルアップ情報も表示) - **コミュニティカード**: 場に公開された共有カード(フェーズに応じて0/3/4/5枚) - **CPUプレイヤーエリア**: 各CPUのプレイスタイル、チップ、ベット額、状態 - ショーダウン時にはCPUのホールカードと役名が表示される - **CPU行動ログ**: CPUが行ったアクションの記録 - **結果エリア**: ショーダウン後、各プレイヤーの役と獲得チップを表示 - **フッター(あなたの手札)**: - ホールカード2枚が表示される - フォールド/オールイン状態のバッジ - ショーダウン時には役名が表示される - **メッセージエリア**: ゲームの状態メッセージ - **アクションボタン**: 手番時のみ表示 ## HUD統計 各プレイヤーにはHUD(ヘッズアップディスプレイ)統計が表示されます: - **VPIP** (Voluntarily Put $ In Pot): プリフロップで自発的にチップを入れた割合(コール/ベット/レイズ/オールイン) - **PFR** (Pre-Flop Raise): プリフロップでレイズした割合(ベット/レイズ/オールイン) 統計はハンド数が1以上のプレイヤーに対してのみ表示されます。 ## トーナメントモード トーナメントモードを有効にすると、指定ハンド数ごとにブラインドが自動的に上昇します。情報バーにハンド数とレベルアップ情報が追加表示されます。 API経由で以下のパラメータを指定できます: - **tournamentMode**: `true` でトーナメントモードを有効化 - **blindLevelHands**: ブラインドレベルアップまでのハンド数(デフォルト10) - **blindMultiplier**: ブラインド倍率(百分率、デフォルト200 = 2倍) - **bettingLimit**: ベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit、デフォルト0) - **tableSize**: テーブルサイズ(4=4-max、6=6-max、9=9-max、デフォルト4) - **rebuyEnabled**: `true` でリバイを有効化(デフォルト`false`) - **rebuyMaxCount**: リバイの最大回数(デフォルト3) - **rebuyChips**: リバイで得られるチップ数(デフォルト1000) - **rebuyPeriodHands**: リバイ可能なハンド数(デフォルト10) - **addonEnabled**: `true` でアドオンを有効化(デフォルト`false`) - **addonChips**: アドオンで得られるチップ数(デフォルト1500) - **addonAfterHand**: アドオン可能になるハンド番号(デフォルト10) - **cpuMetaAI**: `true` でメタAIを有効化(CPUが人間のブラフ率・フォールド率・思考時間を学習して戦略を適応、デフォルト`false`) - **humanPlayMs**: 人間のアクションにかかった時間(ミリ秒)。メタAI有効時にリクエストに含めて送信 ### レスポンスのベッティングリミット関連フィールド | フィールド | 型 | 説明 | |------------|------|------| | `bettingLimit` | int | 現在のベッティングリミット(0=Fixed, 1=PotLimit, 2=NoLimit) | | `raiseCount` | int | 現在のベッティングラウンドでのレイズ回数 | | `maxBetAmount` | int | 現在のベッティングリミットに基づく最大ベット額 | | `tableSize` | int | 現在のテーブルサイズ(4/6/9) | | `rebuyPhaseType` | int | リバイフェーズの種類(1=リバイ、2=アドオン) | | `rebuyChips` | int | リバイで得られるチップ数 | | `rebuyMaxCount` | int | リバイの最大回数 | | `rebuyCounts` | int[] | 各プレイヤーのリバイ使用回数 | | `addonChips` | int | アドオンで得られるチップ数 | | `muckAvailable` | bool | マック選択が可能かどうか(ショーダウンで負けた場合にtrue) | ## マック/ショー ショーダウンでプレイヤーが負けた場合、手札の公開/非公開を選択できます。 - **「マック」ボタン**: 手札を伏せて非公開にする - **「ショー」ボタン**: 手札を公開する マックした場合、結果表示で手札が非表示になります。 ## リバイ/アドオン ### リバイ リバイ期間内にチップが0になった場合、リバイ画面が表示されます。 - **「リバイ」ボタン**: チップを再購入して続行 - **「スキップ」ボタン**: リバイせずに続行 リバイの有効/無効はフッターの「リバイ有効」チェックボックスで切り替えます。 ### アドオン 指定ハンド数経過後にアドオン画面が表示されます(1回のみ)。 - **「アドオン」ボタン**: チップを追加購入 - **「スキップ」ボタン**: アドオンせずに続行 アドオンの有効/無効はフッターの「アドオン有効」チェックボックスで切り替えます。 CPUプレイヤーはリバイ・アドオンを自動的に実行します。 ## ラーニングモード 画面下部の「ラーニングモード」チェックボックスを有効にすると、リアルタイムのエクイティ(勝率)とポットオッズが表示されます。 ### 表示内容 - **エクイティ**: モンテカルロシミュレーション(5000回)による勝率。緑色のバーで視覚的に表示されます - **ポットオッズ**: コール額 / (ポット + コール額) × 100。コールに必要な勝率の目安です - **+EV / -EV インジケーター**: エクイティがポットオッズを上回る場合は +EV(期待値プラス = コールが有利)、下回る場合は -EV(期待値マイナス = フォールドが有利)と表示されます - **ハンドオッズ**: クリックで展開。現在の手札とコミュニティカードから、各ハンドランク(ワンペア、フラッシュ等)になる確率を表示します ### 使い方 エクイティとポットオッズを比較して判断します: - エクイティ > ポットオッズ → コール/レイズが期待値的に有利 - エクイティ < ポットオッズ → フォールドが期待値的に有利 ラーニングモードはプリフロップからリバーまでのアクティブフェーズで利用できます。ショーダウン・終了フェーズでは表示されません。 ## 遊び方のコツ - ショートデックでは36枚のデッキのため、フラッシュがフルハウスより強いです(フラッシュが成立しにくいため) - 最低ストレートはA-6-7-8-9です(2〜5が存在しないため) - デッキが小さいため、ペアやセットが通常のホールデムより成立しやすいです - ポジション(ディーラーからの距離)は重要です。後ろのポジションほど有利です - 強い手(ペア、高いカード)はプリフロップでレイズして主導権を握りましょう - LAGスタイルのCPUはブラフが多いので、良い手を持っているときはコールやレイズで対抗しましょう - GTOスタイルのCPUは確率的な混合戦略を使うため、行動パターンが読みにくいです。ボードテクスチャに注意して対応しましょう - TAGスタイルのCPUがレイズしてきたら、本当に強い手を持っている可能性が高いので注意しましょう - HUD統計を活用して、CPUのプレイスタイルの傾向を把握しましょう - トーナメントモードではブラインドが上がるため、早めにチップを稼ぐ戦略が重要です - コミュニティカードとの組み合わせを常に確認し、フラッシュやストレートの可能性を見逃さないようにしましょう - オールインは最後の手段として使いましょう。全チップを失うリスクがあります # スペード(Web版)遊び方 ## ゲーム概要 4人(プレイヤー1人 + CPU 3人)で遊ぶトリックテイキング型カードゲームです。スペードが常にトランプ(切り札)で、各ラウンド開始時にビッド(獲得予想トリック数)を宣言します。先に500点に到達したプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「スペード」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを4人に配布(1人13枚) - **スペードが常にトランプ(切り札)** - 各ラウンド開始時にビッド(0〜13)を宣言 - ビッド0 = ニル(高リスク・高リターン) - 2♣を持つプレイヤーが最初のトリックをリード - リードスートに従う(フォロースート)。スペードはブレイクされるまでリードできない - トリックの勝者: スペード(トランプ)が出ていれば最高のスペード、なければリードスートの最高値 ### 得点 - **ビッド成功**: ビッド×10 + オーバートリック(バッグ) - **ビッド失敗**: -ビッド×10 - **ニル成功**: +100点 - **ニル失敗**: -100点 - **バッグペナルティ**: 累積10バッグごとに-100点 ### ゲーム終了 - **勝利条件**: 先に500点に到達 - **敗北条件**: -200点以下 ### CPU難易度 - **Easy**: ランダムにカードを出す - **Normal**: 基本的なトリック戦略(デフォルト) - **Hard**: 戦略的なプレイ(カウンティング等) ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各13枚] C --> D[ビッドフェーズ] D --> E[ビッド数を入力してビッドボタン] E --> F[2♣がリード] F --> G{プレイヤーの手番?} G -->|はい| H[手札のカードをクリックして選択] G -->|いいえ| I[CPUが自動でカードを出す] H --> J[出すボタンをクリック] J --> K[トリック完了] I --> K K -->|次のトリックボタン| L{13トリック終了?} L -->|いいえ| G L -->|はい| M[ラウンド終了 - 得点計算] M -->|次のラウンドボタン| N{勝利/敗北条件を満たした?} N -->|いいえ| C N -->|はい| O[ゲーム終了 - 結果表示] O -->|リセットボタン| B ``` ## 画面の操作方法 ### ビッドフェーズ 1. 数値入力欄にビッド数(0〜13)を入力します 2. **「ビッド」** ボタンで確定します 3. 0を入力するとニルビッドになります ### カードのプレイ(プレイフェーズ) 1. 手札のカードをクリックして選択します 2. フォロー必須のルールに従い、出せないカードは選択できません 3. **「カードを出す」** ボタンをクリックしてカードを場に出します ### ヒント - ビッドフェーズまたはプレイフェーズで **「ヒント」** ボタンをクリックすると、CPUのHard戦略に基づく推奨が表示されます - ビッドフェーズでは推奨ビッド数、プレイフェーズでは推奨カードが黄色のメッセージで表示されます ### トリック・ラウンドの進行 1. トリックが完了すると結果が表示されます 2. **「次のトリック」** ボタンで次のトリックに進みます 3. ラウンドが完了すると得点が表示されます 4. **「次のラウンド」** ボタンで次のラウンドに進みます ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`9`, `0` | カード選択/解除(1=1枚目, ..., 9=9枚目, 0=10枚目) | プレイヤーの手番 | | `Enter` | カードを出す / ビッド確定 | プレイヤーの手番 / ビッドフェーズ | | `Escape` | 選択クリア | プレイヤーの手番 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ▶ 設定 (クリックで展開) │ │ CPU難易度: [Normal▼] │ │ 目標スコア: [500▼] │ ├────────────────────────────────────────────┤ │ CPU プレイヤーエリア │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ CPU 1 │ │ CPU 2 │ │ CPU 3 │ │ │ │ 80点 │ │ 150点 │ │ 60点 │ │ │ │ B:3 W:2 │ │ B:5 W:5 │ │ B:0(Nil) │ │ │ └──────────┘ └──────────┘ └──────────┘ │ ├────────────────────────────────────────────┤ │ ゲーム情報 │ │ ラウンド 2 / トリック 5 │ │ スペードブレイク: はい │ ├────────────────────────────────────────────┤ │ 現在のトリック │ │ CPU 1: ♣10 CPU 2: ♣K │ ├────────────────────────────────────────────┤ │ フッター: あなた (120点, B:4 W:3) │ │ [♠5][♣8][♥K][♦3]... │ │ [リセット] [ヒント] [カードを出す] [次のトリック] │ └────────────────────────────────────────────┘ ``` - **設定パネル(▶ 設定)**: - 「CPU難易度」セレクト: Easy / Normal / Hard から選択 - 「目標スコア」: 200 / 300 / 500 / 750 / 1000 から選択 - 次のリセット時に設定が適用される - **CPU プレイヤーエリア**: 各 CPU の累計得点、ビッド数、獲得トリック数を表示 - 手番の CPU は金色枠 - **ゲーム情報**: 現在のラウンド、トリック番号、スペードブレイク状態 - **現在のトリック**: トリックに出されたカード - **フッター(あなたの手札)**: - クリックで選択(緑枠 + 上に浮き上がる) - フォロー必須のカードのみ選択可能 - **スコアテーブル**: 各プレイヤーのビッド、獲得トリック数、バッグ数、ラウンドスコア、累積スコア ## 遊び方のコツ - スペードの高いカード(A♠、K♠)を持っている場合はビッドを多めに設定しましょう - ニルビッドは手札に高いカードが少ない場合のみ狙いましょう - バッグ(オーバートリック)が累積10に近い場合は、トリックを取りすぎないよう注意しましょう - 特定のスートをボイド(手札からなくす)にして、スペードで切る戦略が有効です - 他のプレイヤーのビッドと獲得トリック数を把握し、戦略を調整しましょう - 迷った時はヒントボタンでCPUのHard戦略に基づく推奨を確認できます # スピード(Web版)遊び方 ## ゲーム概要 2人(プレイヤー1人 + CPU 1人)で遊ぶスピード系カードゲームです。中央の2つの場札に対して、ランクが±1のカードを素早く出していき、先に手札と山札をすべてなくしたプレイヤーが勝者です。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「スピード」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプを2人に26枚ずつ配布 - 各プレイヤーは手札(最大4枚)と山札を持つ - 中央に2つの場札がある - 場札のランク±1のカードを手札から出せる(K→Aのラップあり) - カードを出したら山札から手札を補充する - 先に手札と山札をすべてなくしたプレイヤーが勝利 ### スタック(行き詰まり) - 両プレイヤーが出せるカードがない場合、スタック状態になる - スタック時は「めくる」ボタンで中央の場札に新しいカードをめくる - 山札がない場合はゲーム終了 ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カード配布 - 各26枚] C --> D[手札4枚 + 中央場札2枚を配置] D --> E{プレイヤーの手番} E --> F[手札カードをクリックして選択] F --> G[場札をクリックしてカードを出す] G --> H[山札から手札を自動補充] H --> I[CPU自動プレイ] I --> J{手札+山札が空?} J -->|はい| K[勝者確定・ゲーム終了表示] J -->|いいえ| L{出せるカードあり?} L -->|はい| E L -->|いいえ| M{両者スタック?} M -->|はい| N[めくるボタンで新しい場札をめくる] M -->|いいえ| E N --> E K -->|リセットボタン| B ``` ## 画面の操作方法 ### カードの出し方(プレイフェーズ) 1. 画面下部の手札エリアに自分のカードが表示されます 2. 出したいカードをクリックして選択します(緑枠でハイライト) 3. 中央の場札(左または右)をクリックしてカードを出します 4. 場札のランク±1でないカードは出せません ### スタック時の操作 1. 両プレイヤーが出せるカードがない場合、「スタック」状態になります 2. **「めくる」** ボタンをクリックして中央の場札に新しいカードをめくります ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `1`〜`4` | 手札カード選択(1=1枚目, ..., 4=4枚目) | プレイフェーズ | | `←` / `→` | 左/右の場札にカードを出す | カード選択中 | | `Enter` | めくる | スタックフェーズ | ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ CPU プレイヤーエリア │ │ 手札: 4枚 山札: 18枚 │ │ [?][?][?][?] │ ├────────────────────────────────────────────┤ │ 中央場札エリア │ │ [♥7] [♠10] │ │ (場札 0) (場札 1) │ ├────────────────────────────────────────────┤ │ フッター: あなた │ │ 手札: 4枚 山札: 18枚 │ │ [♣6][♦8][♥3][♠Q] │ │ [リセット] [ヒント] [めくる] │ └────────────────────────────────────────────┘ ``` - **CPU プレイヤーエリア**: CPUの手札枚数と山札残り枚数を表示(カードは裏向き) - **中央場札エリア**: 2つの場札の表面を表示。クリックしてカードを出す先を選択 - **フッター(あなたの手札)**: - 手札カードをクリックで選択(緑枠ハイライト) - 「リセット」ボタン: ゲームを最初からやり直す - 「ヒント」ボタン: 出せるカードの候補を表示 - 「めくる」ボタン: スタック時に新しい場札をめくる(スタック時のみ有効) ## CPU難易度 設定でCPUの強さを3段階から選べます。 | レベル | 名称 | 戦略 | |--------|------|------| | 0 | Easy | ランダムに1枚だけ出す | | 1 | Normal | 貪欲に複数枚を連続で出す | | 2 | Hard | ブロッキング(相手の手を封じる出し方)とコンボ(連鎖プレイ)を考慮して戦略的に出す | ## 遊び方のコツ - 場札の±1に合うカードを素早く見つけて出しましょう - K と A はラップするので、K の場には A を、A の場には K を出せます - 両方の場札に出せるカードがある場合は、戦略的にどちらに出すか考えましょう - スタックが頻発する場合は、めくりで流れが変わることがあります - ヒントボタンで迷ったときに出せるカードを確認できます # スパイダーソリティア(Web版)遊び方 ## ゲーム概要 1人用のソリティアゲーム。2デッキ(104枚)を使い、10列のタブローで同スート降順のシーケンスを完成させ、K〜Aの13枚を8組すべて除去すれば勝利です。難易度は1/2/4スートから選択できます。 ## アクセス方法 ```sh go run ./cmd/trumpcards web # ブラウザで http://localhost:8080/#/spider にアクセス ``` ## ルール ### 基本ルール - 2デッキ104枚を使用 - 10列のタブローに配る(最初の4列は6枚、残り6列は5枚、各列の最後の1枚だけ表向き) - 残り50枚はストック(5回に分けて10枚ずつ配る) - タブロー間でカードを移動する。値が1つ大きいカードの上に置ける(スート不問) - 同スート降順の連続シーケンスのみグループ移動可能 - K〜Aの同スート13枚が揃うと自動的に除去される - 8組すべて除去でゲームクリア ### 難易度 - **1スート(初級)**: スペードのみの104枚 - **2スート(中級)**: スペートとハートの104枚 - **4スート(上級)**: 全4スートの104枚(2デッキ分) ### 得点 - 開始時: 500点 - 移動/配り: -1点 - スート完成: +100点 ## 画面構成 ### ヘッダー - フェーズ表示(プレイ中 / ゲームクリア / ゲームオーバー) - 手数、スコア、完成スート数 ### メインエリア - **ストック**: クリックで各列に1枚ずつ配る(残り回数表示) - **タブロー(10列)**: カードをクリックで選択→移動先をクリック ### フッター - 配る、元に戻す、ヒント、自動完成、ギブアップボタン - 難易度セレクター - リセットボタン ## 操作方法 ### マウス操作 1. 移動元のカードをクリックして選択(黄色の枠で強調) 2. 移動先の列をクリックして移動 3. ストックのカード裏面をクリックで配る ### キーボードショートカット | キー | 操作 | |------|------| | `d` | ストックから配る | | `h` | ヒントを表示 | | `a` | 自動完成 | | `g` | ギブアップ | | `z` | 元に戻す | ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始] --> B[プレイ中] B --> C{操作選択} C -->|カード移動| D[タブロー間移動] C -->|ストッククリック| E[各列に1枚配る] C -->|ヒントボタン| F[ヒント表示] C -->|自動完成ボタン| G[完成スート自動除去] C -->|ギブアップ| H[ゲームオーバー] D --> I{スート完成?} I -->|Yes| J{8組完成?} I -->|No| B J -->|Yes| K[ゲームクリア 🎉] J -->|No| B E --> B H --> L[ゲームオーバー] ``` ## 遊び方のコツ - まず1スート(初級)で慣れましょう - 裏カードを開けることを優先する - 空列を作ると移動の自由度が上がる - ストックを配る前に空列をなくすこと(空列があるとストックから配れない) - 同スートの降順シーケンスを意識して組み立てる - ヒント機能を活用して有効な手を見つける - 元に戻す機能で試行錯誤する # スリーカードポーカー(Web版)遊び方 ## ゲーム概要 カジノの人気カードゲームです。3枚のカードでディーラーと勝負します。アンティベットとオプションのペアプラスサイドベットを組み合わせてプレイします。 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「スリーカードポーカー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - プレイヤーとディーラーにそれぞれ3枚ずつ配る - プレイヤーはアンティベット後、自分の手札を見てプレイまたはフォールドを決定 - ディーラーはQ-ハイ以上で「クオリファイ(資格あり)」 ### 手札ランキング(強い順) | ランク | 説明 | |--------|------| | ストレートフラッシュ | 同じスートの連続3枚 | | スリーオブアカインド | 同じランク3枚 | | ストレート | 連続する3枚(スート不問) | | フラッシュ | 同じスートの3枚(連続でない) | | ペア | 同じランク2枚 | | ハイカード | 上記以外 | ### ベットタイプと配当 **アンティ/プレイベット:** | 条件 | アンティ配当 | プレイ配当 | |------|-------------|-----------| | ディーラー不資格 | 1:1 | プッシュ(返却) | | プレイヤー勝ち | 1:1 | 1:1 | | ディーラー勝ち | 没収 | 没収 | | 引き分け | プッシュ | プッシュ | **アンティボーナス(プレイヤーの手札に対して自動支払い):** | 役 | 配当 | |----|------| | ストレートフラッシュ | 5:1 | | スリーオブアカインド | 4:1 | | ストレート | 1:1 | **ペアプラス(サイドベット、勝敗に関係なく支払い):** | 役 | 配当 | |----|------| | ストレートフラッシュ | 40:1 | | スリーオブアカインド | 30:1 | | ストレート | 6:1 | | フラッシュ | 3:1 | | ペア | 1:1 | ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[ベットフェーズ] B --> C[アンティ額とペアプラス額を入力] C --> D[ベットボタンをクリック] D --> E[3枚のカードが表示される] E --> F{プレイ or フォールド?} F -->|プレイボタン| G[プレイベット確定] F -->|フォールドボタン| H[アンティ・ペアプラス没収] G --> I[ディーラーのカード公開] I --> J[結果表示] H --> J J -->|リセットボタン| B ``` ## 画面の操作方法 ### ベットフェーズ 1. **アンティ額**: 数値入力欄でアンティベット額を設定(10刻み、最低10、最大は所持チップ数) 2. **ペアプラス額(任意)**: ペアプラスサイドベット額を設定(0で無効) 3. **「ベット」ボタン**: クリックでベットを確定し、カードが配られる ### アクションフェーズ 1. プレイヤーの3枚のカードと役名が表示されます 2. **「プレイ」ボタン**: アンティと同額のプレイベットを置いてディーラーと勝負 3. **「フォールド」ボタン**: アンティとペアプラスを放棄 ### 結果フェーズ 1. プレイヤーとディーラーのカードが表示されます 2. ディーラーの資格有無が表示されます 3. 結果メッセージと各配当(アンティ、プレイ、アンティボーナス、ペアプラス)が表示されます 4. **「リセット」ボタン**: 新しいラウンドを開始 5. **「棋譜を見る」ボタン**: アクションログを表示 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `b` | ベット | ベットフェーズのみ | | `p` | プレイ | アクションフェーズのみ | | `f` | フォールド | アクションフェーズのみ | | `r` | リセット | 結果フェーズのみ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ チップ: 1000 │ ├────────────────────────────────────────────┤ │ プレイヤーの勝ち! │ │ │ │ あなたの手札: ペア │ │ [♠K] [♥K] [♦9] │ │ │ │ ディーラー: ハイカード │ │ [♣J] [♦8] [♠5] │ │ ディーラー不資格 │ │ │ │ アンティ配当: 200 プレイ返却: 100 │ │ ペアプラス配当: 100 │ │ 合計配当: 400 │ ├────────────────────────────────────────────┤ │ [リセット] [棋譜を見る] │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ - Q-6-4以上の手札でプレイするのが最適戦略です - ペアプラスはハウスエッジが高め(約7.3%)ですが、高配当のチャンスがあります - アンティ/プレイのハウスエッジは約3.4%で比較的低めです - アンティボーナスはプレイヤーに有利な追加配当なので、良い手はプレイしましょう # トリピークス(Web版)遊び方 ## ゲーム概要 1人用のソリティアカードゲームです。52枚のカードを使い、3つの重なり合うピラミッド(ピークス)に並べた28枚のカードから、ウェイストのトップカードと±1のランクのカードを除去していきます。タブローのカードをすべて除去すればクリアです。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「トリピークス」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 3つの重なり合うピラミッド型に28枚を配る - 残りの24枚のうち1枚をウェイストに、23枚を山札(ストック)に置く - 「露出カード」(下の段のカードが除去済み)のみ選択可能 ### 除去ルール - ウェイストのトップカードと±1のランクのカードを除去できる - K-Aは繋がる(ラップアラウンド):KからAへ、AからKへ除去可能 - 例:ウェイストが5なら、4または6を除去できる ### ゲームクリア - タブローの28枚すべてを除去するとゲームクリア ### ゲームオーバー - これ以上除去可能な手がない場合はゲームオーバー ### 手詰まり検出 - ヒントが存在せず、かつ山札が空の場合に「手詰まりです」とメッセージが表示されます - 手詰まり状態では「元に戻す」またはギブアップを選択できます ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[カードを配る] C --> D{除去可能な手がある?} D -->|はい| E{プレイヤーのアクション} E -->|山札クリック| F[山札からウェイストにカードを引く] E -->|カードをクリック| G[カードを選択・除去] E -->|ヒントボタン| H[ヒントを表示] E -->|元に戻すボタン| I[直前の操作を取り消す] F --> D G --> J{タブロー全除去?} H --> D I --> D J -->|いいえ| D J -->|はい| K[ゲームクリア] D -->|いいえ| L[ゲームオーバー] K -->|リセットボタン| B L -->|リセットボタン| B ``` ## 画面の操作方法 ### カードを除去する 1. 露出カードをクリックして除去します(ウェイストトップと±1のランクである必要あり) 2. K-Aのラップアラウンドも有効です ### 山札を引く 1. 山札エリアをクリックしてカードをウェイストに引きます 2. 山札が空になると引けなくなります ### アンドゥ(元に戻す) **「元に戻す」** ボタンまたは `z` キーで直前の操作を取り消せます。何度でも元に戻せます。 ### ヒント **「ヒント」** ボタンをクリックすると、次に可能な手を提案します。 ### キーボードショートカット ゲーム中、以下のキーボードショートカットが使用できます。 | キー | アクション | 有効条件 | |------|-----------|---------| | `d` | カードを引く(ドロー) | プレイ中 | | `z` | 元に戻す(アンドゥ) | プレイ中 | | `h` | ヒント | プレイ中 | | `g` | ギブアップ | プレイ中 | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ゲーム情報 │ │ 手数: 0 │ ├────────────────────────────────────────────┤ │ タブローエリア │ │ [♥A] [♦K] [♣7] │ │ [♠3] [♥8] [♦2] [♣9] [♠6] [♥Q] │ │ [♦4] [♣J] [♠10] ... [♥9] [♦8] │ │ [♣5] [♠K] [♥7] ... [♣4] [♠9] │ ├────────────────────────────────────────────┤ │ 山札・ウェイストエリア │ │ [山札: 23枚] [ウェイスト: ♠5] │ ├────────────────────────────────────────────┤ │ [引く] [元に戻す] [ヒント] │ │ [ギブアップ] [リセット] │ └────────────────────────────────────────────┘ ``` - **タブローエリア**: 3つの重なり合うピラミッド型にカードを表示 - 露出カード: クリックで除去可能(明るく表示) - 非露出カード: クリック不可(暗く表示) - 除去済み: 空白表示 - **山札エリア**: クリックでカードをウェイストに引く(残りカード数表示) - **ウェイストエリア**: 引いたカードが表示される(±1のランクのカードを除去する基準) - **ボタン**: - 「引く」: 山札からカードを引く - 「元に戻す」: 直前の操作を取り消す - 「ヒント」: 次の一手を提案 - 「ギブアップ」: 現在のゲームを終了 - 「リセット」: 新しいゲームを開始 ## 遊び方のコツ - ウェイストのトップカードから連続して除去できるチェーンを狙いましょう - K-Aのラップアラウンドを活用して長いチェーンを作りましょう - 上段のカードを露出させるために、下段のカードを優先的に除去します - 山札を引く前に、タブロー内で除去できるカードがないか確認しましょう - ヒントボタンで詰まった時のアドバイスを得られます - アンドゥを活用して、異なる戦略を試しましょう # ビデオポーカー(Web版)遊び方 ## ゲーム概要 1人用カジノカードゲームです。52枚のトランプを使い、5枚のカードでポーカーの役を作ります。Jacks or Better(J以上のワンペアから配当)のペイテーブルに基づいて配当が支払われます。 ## 起動方法 ```sh go run ./cmd/trumpcards web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「ビデオポーカー」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ### 基本ルール - 52枚のトランプ(ジョーカーなし)を使用 - 1〜5コインをベットしてゲーム開始 - 5枚のカードが配られる - 残したいカード(ホールド)を選び、残りを交換 - 最終的な5枚の手札で役を判定し、配当を支払い ### 配当表(Jacks or Better) | 役 | 1コイン | 2コイン | 3コイン | 4コイン | 5コイン | |----|---------|---------|---------|---------|---------| | ロイヤルフラッシュ | 250 | 500 | 750 | 1000 | 4000 | | ストレートフラッシュ | 50 | 100 | 150 | 200 | 250 | | フォーカード | 25 | 50 | 75 | 100 | 125 | | フルハウス | 9 | 18 | 27 | 36 | 45 | | フラッシュ | 6 | 12 | 18 | 24 | 30 | | ストレート | 4 | 8 | 12 | 16 | 20 | | スリーカード | 3 | 6 | 9 | 12 | 15 | | ツーペア | 2 | 4 | 6 | 8 | 10 | | ジャックスオアベター | 1 | 2 | 3 | 4 | 5 | ### ジャックスオアベター - J、Q、K、Aのワンペア以上が配当の最低条件 - 10以下のワンペアは配当なし ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[ベットフェーズ] B --> C[コイン数を選択] C --> D[ベットボタンをクリック] D --> E[5枚のカードが表示される] E --> F[ドローフェーズ] F --> G[ホールドするカードをクリックで選択] G --> H[ドローボタンをクリック] H --> I[交換後のカードと役判定・配当を表示] I -->|リセットボタン| B ``` ## 画面の操作方法 ### ベットフェーズ 1. **コイン数選択**: コインセレクター(1〜5)でベット額を設定 2. **「ベット」ボタン**: クリックでベットを確定し、5枚のカードが配られる ### ドローフェーズ 1. **カード選択**: ホールドしたいカードをクリックして選択(選択したカードはハイライト表示) 2. **再クリック**: 選択を解除 3. **「ドロー」ボタン**: ホールドしていないカードを交換し、役を判定 ### 結果フェーズ 1. 最終的な5枚のカードと役名が表示されます 2. 配当額が表示されます 3. **「リセット」ボタン**: 新しいラウンドを開始 4. **「棋譜を見る」ボタン**: アクションログを表示 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| | `b` | ベット | ベットフェーズのみ | | `d` | ドロー | ドローフェーズのみ | | `r` | リセット | 結果フェーズのみ | ※ テキスト入力欄にフォーカスがある場合、ショートカットは無効になります。 ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ チップ: 100 │ ├────────────────────────────────────────────┤ │ 配当表 │ │ ロイヤルフラッシュ 250 500 750 1000 4000 │ │ ... │ │ ジャックスオアベター 1 2 3 4 5 │ ├────────────────────────────────────────────┤ │ │ │ [♠A] [♥5] [♦K] [♣3] [♠J] │ │ HOLD HOLD HOLD │ │ │ │ ジャックスオアベター 配当: 3 │ ├────────────────────────────────────────────┤ │ [ドロー] [棋譜を見る] │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ - 5コインベットでロイヤルフラッシュの配当が大幅に増える(250→4000)ため、可能なら最大ベットがお得 - 高いペア(J以上)は必ずホールドする - ストレートやフラッシュのドロー(あと1枚で役が完成)は積極的に狙う - ローペア(10以下)でもスリーカード以上を狙えるのでホールドする価値がある # <ゲーム名>(CUI版)遊び方 ## ゲーム概要 ## 起動方法 ```sh go run ./cmd/trumpcards go run ./cmd/trumpcards --lang en # 英語モード ``` ## ルール ## ゲームの流れ ```mermaid flowchart TD A[ゲーム開始 - reset] --> B[...] B --> C{...} ``` ## コマンド一覧 | コマンド | 短縮形 | 説明 | |----------|--------|------| | `reset` | `r` | 新しいゲームを開始 | | `quit` | `q` | ゲーム終了 | | `help` | `?` | コマンド一覧を表示 | ## 画面の見方 ``` ========== ========== ... ``` ## 遊び方のコツ # <ゲーム名>(Web版)遊び方 ## ゲーム概要 ## 起動方法 ```sh go run ./cmd/cli web # CLI経由でWebサーバーを起動 go run ./cmd/server # 直接Webサーバーを起動 ``` ブラウザで `http://localhost:8080` にアクセスし、ナビゲーションバーから「<ゲーム名>」を選択します。 ナビバーのJA/ENボタンで日本語・英語を切り替えられます。 ## ルール ## ゲームの流れ ```mermaid flowchart TD A[画面を開く] --> B[リセットボタンでゲーム開始] B --> C[...] ``` ## 画面の操作方法 ### キーボードショートカット | キー | アクション | 有効条件 | |------|-----------|---------| ## 画面構成 ``` ┌────────────────────────────────────────────┐ │ ... │ └────────────────────────────────────────────┘ ``` ## 遊び方のコツ # Architecture The project implements Clean Architecture with strict layer dependency rules (outer depends on inner, never the reverse). The directory layout follows `golang-standards/project-layout` (`cmd/` + `internal/`): ``` cmd/ trumpcards/ # CLI entrypoint (all games + web server) server/ # Web server dedicated entrypoint workers/ # Cloudflare Workers WASM entrypoints casino/main.go # Table & poker games classic/main.go # Trick-taking & matching games solo/main.go # Solitaire & rummy games internal/ domain/ # Core business logic (innermost) usecase/ # Application business rules (interactors) presenter/ # Presenter interfaces defined here adapter/ # Convert data between layers controller/ # Route commands to use cases presenter/ # Implement presenter interfaces for CUI and Web infrastructure/ # Outermost layer ui/ # CLI runner web/ # REST API server (go-json-rest) api/ # OpenAPI specification frontend/ # React frontend source (Vite + React + TypeScript) src/ api/ # API client functions (fetch wrappers for game endpoints) components/ # Shared React components (NavBar, CardImage, CardBack) cli/ # CLI mode components (CliTerminal, CliToggle) hooks/ # Custom React hooks (useGameApi, backed by TanStack React Query) i18n/ # i18n config and translation files (ja/en) pages/ # Game page components (BlackJackPage, PokerPage, OldMaidPage) providers/ # React context providers (QueryProvider for TanStack React Query) types/ # TypeScript type definitions for card/game data utils/cli/ # CLI mode utilities commands/ # Per-game command parsers (text input → API args) formatters/ # Per-game response formatters (JSON → terminal text) e2e/ # Playwright E2E test specs public/ # Built frontend assets served by Go web server assets/ # Vite-compiled JS/CSS bundles images/ # Card images (PNG) css/ # Bootstrap CSS ``` **Data flow:** `infrastructure` -> `adapter` -> `usecase` -> `domain` ## Key patterns - **Presenter pattern**: `internal/usecase/presenter/` defines output interfaces (e.g., `BlackJackPresenter`). `internal/adapter/presenter/` provides concrete implementations (CUI vs Web). Presenters are injected into interactors. - **Mock presenters**: `*_mock.go` files in `internal/usecase/presenter/` are used in tests to avoid I/O. - **Web API**: Thirty-nine endpoints -- `POST /blackjack/exec` (BlackJack), `POST /poker/exec` (Poker), `POST /oldmaid/exec` (Old Maid), `POST /daifugo/exec` (Daifugo), `POST /sevens/exec` (Sevens), `POST /doubt/exec` (Doubt), `POST /holdem/exec` (Texas Hold'em), `POST /omaha/exec` (Omaha Hold'em), `POST /shortdeck/exec` (Short Deck Hold'em), `POST /pineapple/exec` (Pineapple Poker), `POST /hearts/exec` (Hearts), `POST /memory/exec` (Memory), `POST /klondike/exec` (Klondike), `POST /freecell/exec` (FreeCell), `POST /baccarat/exec` (Baccarat), `POST /spades/exec` (Spades), `POST /crazyeights/exec` (Crazy Eights), `POST /ginrummy/exec` (Gin Rummy), `POST /spider/exec` (Spider Solitaire), `POST /napoleon/exec` (Napoleon), `POST /indianpoker/exec` (Indian Poker), `POST /videopoker/exec` (Video Poker), `POST /deuceswild/exec` (Deuces Wild), `POST /jokerpoker/exec` (Joker Poker), `POST /euchre/exec` (Euchre), `POST /pyramid/exec` (Pyramid), `POST /tripeaks/exec` (TriPeaks), `POST /cribbage/exec` (Cribbage), `POST /threecard/exec` (Three Card Poker), `POST /ohhell/exec` (Oh Hell), `POST /bridge/exec` (Contract Bridge), `POST /speed/exec` (Speed), `POST /gofish/exec` (Go Fish), `POST /canasta/exec` (Canasta), `POST /pinochle/exec` (Pinochle), `POST /golf/exec` (Golf Solitaire), `POST /pigtail/exec` (Pig's Tail), `POST /sevencardstud/exec` (Seven Card Stud), and `POST /clocksolitaire/exec` (Clock Solitaire) -- accept JSON with a `Cmd` field and game state. - **Swagger UI**: Available at `/swagger/` -- serves the OpenAPI spec (`api/openapi.yaml`) via Swagger UI for interactive API documentation and testing. The spec is embedded into the binary with `go:embed`; the Swagger UI frontend is loaded from a CDN. ## Cloudflare Workers (WASM) deployment In addition to Docker deployment (Render), the project supports edge deployment via Cloudflare Workers. See [ADR-0027](adr/0027-cloudflare-workers-wasm.md) and [ADR-0028](adr/0028-kv-session-persistence.md) for decision rationale. ### Build toolchain Go source (`cmd/workers/*/main.go`) is compiled to WASM using **TinyGo** (not the standard Go compiler) to meet Cloudflare's 1MB compressed size limit. The pipeline is: ``` Go source (//go:build js && wasm) → TinyGo compiler (tinygo build -target wasm) → wasm-opt (size optimisation) → worker.mjs + wasm_exec.js (JS runtime wrapper, generated by workers-assets-gen) → wrangler deploy (Cloudflare Workers) ``` Build commands: `make build-worker-{solo,casino,classic}` or `make build-workers`. ### 3-Worker split Games are distributed across three Workers to stay under the 1MB gzip size limit per Worker: | Worker | Category | Entry point | Games | |--------|----------|-------------|-------| | **casino** | Table & poker | `cmd/workers/casino/main.go` | blackjack, baccarat, poker, holdem, omaha, shortdeck, pineapple, indianpoker, videopoker, deuceswild, jokerpoker, threecard, sevencardstud | | **classic** | Trick-taking & matching | `cmd/workers/classic/main.go` | hearts, spades, euchre, napoleon, oldmaid, doubt, daifugo, sevens, crazyeights, ohhell, bridge, speed, gofish, pinochle, pigtail | | **solo** | Solitaire & rummy | `cmd/workers/solo/main.go` | klondike, freecell, spider, pyramid, tripeaks, memory, ginrummy, canasta, cribbage, golf, clocksolitaire | The frontend routes requests to the correct Worker via `workerUrl` mapping in `frontend/src/api/gameApi.ts`. When `VITE_WORKER_*_URL` env vars are unset, requests fall back to relative URLs (Docker deployment). ### Session persistence Workers are stateless, so game sessions are persisted in **Cloudflare KV** (`GAME_SESSIONS` namespace). Each game's domain object implements `MarshalJSON`/`UnmarshalJSON` for serialisation. The `SessionProvider[T]` interface abstracts session storage: - **Docker**: `MemorySessionProvider` (in-memory `SessionStore[T]`) - **Workers**: `KVSessionProvider` (Cloudflare KV, TTL=1h, JSON serialised) ### Adding a game to Workers When adding a new game, register it in the appropriate Worker entry point using `registerKV`: ```go // cmd/workers//main.go registerKV(mux, "//exec", ":", func() usecase.InteractorIF { ... }, // factory func(data []byte) (usecase.InteractorIF, error) { ... }, // restore func(p controller.SessionProvider[usecase.InteractorIF], f func() usecase.InteractorIF) interface { ... } { ... }, // controller ) ``` Also ensure `frontend/src/api/gameApi.ts` `workerUrl` maps the game to the correct `WORKER_*` constant. ### TinyGo constraints - `go.mod` specifies `go 1.25.8` (TinyGo's latest supported Go version) with `toolchain go1.26.0` for local development - Mock files require `//go:build test` tag to exclude `testify/mock` from WASM builds - `net/http` method-prefixed routing (`"POST /path"`) is not supported; Worker entry points use plain `"/path"` patterns # Games Implemented - **BlackJack**: Entities in `internal/domain/BlackJack.go`, `internal/domain/BlackJackPlayer.go`, `internal/domain/BlackJackHand.go`, `internal/domain/BlackJackSideBet.go`; interactor in `internal/usecase/BlackJackInteractor.go`. Features chip/betting system, split, double down, insurance, natural BJ 3:2 payout, soft-17 rule toggle (H17/S17), double-after-split toggle (DAS), configurable deck penetration (50%/75%), card counting training (Hi-Lo / KO / Zen Count / Omega II running count / true count display with selectable counting system), multi-player CPU seats (0-3 CPU players using basic strategy with card-counting AI: when counting is enabled, CPUs vary bet amounts based on true/running count using a bet spread and take insurance at count >= 3), side bets (Perfect Pairs and 21+3), multi-hand play (1-3 simultaneous hands per round for card counting practice), auto-advance round timer, and configurable surrender rule (0=Late surrender default, 1=Early surrender with pre-deal decision, 2=No surrender) - **Poker (5-card Draw)**: Entities in `internal/domain/Poker.go`, `internal/domain/PokerPlayer.go`, `internal/domain/PokerConfig.go`, `internal/domain/PokerOdds.go`; interactor in `internal/usecase/PokerInteractor.go`. CLI and Web GUI (1 human vs 1-3 CPU), 4 CPU play styles (Conservative/Balanced/Aggressive/Bluffer) with exchange-count reading and bluff AI, optional joker wild cards (0-2, Five of a Kind rank), full side pot support, draw odds calculator (brute-force enumeration of all combinations during exchange phase), configurable betting limit (Fixed/Pot Limit/No Limit), kicker display at showdown, 2-7 Lowball mode toggle (lowest hand wins; Ace always counts as 14, straights and flushes count against the player, best possible hand is 2-3-4-5-7 off-suit, jokers are forced to rank 0), Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate, fold rate, and hesitation timing across games and adjusts CPU betting/folding strategy accordingly) - **Old Maid (Babanuki)**: Entities in `internal/domain/OldMaid.go`, `internal/domain/OldMaidPlayer.go`, `internal/domain/OldMaidConfig.go`; interactor in `internal/usecase/OldMaidInteractor.go`. Optional rules: CPU placement strategy (odd card at edges), CPU memory AI (remember draw positions and adjust selection strategy based on pair results and human hand changes), Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's edge-pick rate across games and adjusts CPU strategy accordingly). Features: draw history timeline (persistent log of all draws throughout the game), suspect pin (client-side toggle to mark CPU players suspected of holding the odd card) - **Daifugo**: Entities in `internal/domain/Daifugo.go`, `internal/domain/DaifugoPlayer.go`; interactor in `internal/usecase/DaifugoInteractor.go`. Supports optional rules: sandstorm (3 non-joker 3s clear the table like 8-cut), emperor (4 consecutive cards of all different suits on clear table triggers revolution + table clear, CPU AI can find emperor plays), sequence revolution (4+ card sequences trigger revolution), sequence lock (playing a sequence on top of another sequence activates lock; persists across table clears and only resets on game Reset; when locked and table is clear only sequences of 3+ cards or emperor can be played), illegal finish (finishing with 8-cut/joker/revolution is penalized -- player demoted to last rank, CPU AI avoids when safe alternative exists), 12 bomber (playing Q lets you choose a number and removes all cards of that number from all players), configurable suit lock mode (none/partial/full 3-level selection), number lock (independent from suit lock -- consecutive same-number plays restrict to that number), configurable 5-skip count (set how many players are skipped when 5 is played, 1-5), blind card exchange (upper-rank player gives random cards instead of their weakest during card exchange). Three CPU difficulty levels: Normal (default, balanced AI with card preservation), Easy (simple greedy play), Hard (strategic AI that adapts to opponent hand sizes and plays aggressively when opponents are close to finishing) - **Sevens (7並べ)**: Entities in `internal/domain/Sevens.go`, `internal/domain/SevensPlayer.go`, `internal/domain/SevensConfig.go`; interactor in `internal/usecase/SevensInteractor.go`. Supports optional rules: tunnel (A↔K circular), joker, CPU strategy (3 modes: simple/strategic/harassment), configurable max passes (0 = unlimited), no-joker-finish (ban finishing with a joker), joker reclaim (playing a real card on a joker-occupied position returns the joker to the player's hand), end stop (A/K止め -- placing A blocks high side 8-K, placing K blocks low side A-6), and joker consecutive banned (ジョーカー連続禁止 -- prevents playing joker on consecutive turns). CPU AI uses pass-urgency weighting to block opponents near pass exhaustion. Harassment mode prioritizes blocking opponent progress over self-benefit - **Doubt (ダウト)**: Entities in `internal/domain/Doubt.go`, `internal/domain/DoubtPlayer.go`; interactor in `internal/usecase/DoubtInteractor.go`. CLI and Web GUI (1 human vs 3 CPUs), 10-second async doubt window (CLI) / frontend countdown timer (Web), random CPU bluff/doubt AI, configurable penalty draw limit (loser picks up at most N cards; excess discarded from game), Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate and doubt accuracy across games and adjusts CPU bluff/doubt strategy accordingly) - **Texas Hold'em**: Entities in `internal/domain/Holdem.go`, `internal/domain/HoldemPlayer.go`, `internal/domain/HoldemConfig.go`; interactor in `internal/usecase/HoldemInteractor.go`. CLI and Web GUI (1 human vs 3–8 CPU, configurable 4/6/9-max table sizes), 5 CPU play styles (TAG/LAP/TAP/LAG/GTO) with bluff AI and GTO mixed strategy, full side pot support, HUD stats (VPIP%/PFR%/3Bet%/AF), pot-relative AI bet sizing, tournament mode with blind escalation, configurable betting limit (Fixed/Pot Limit/No Limit), kicker display at showdown, muck/show (hide or reveal hand after losing at showdown), rebuy/addon system (configurable max count, chips, eligibility period), Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate, fold rate, and hesitation timing across games and adjusts CPU betting/folding strategy accordingly) - **Omaha Hold'em**: Entities in `internal/domain/Omaha.go`, `internal/domain/OmahaPlayer.go`, `internal/domain/OmahaConfig.go`, `internal/domain/OmahaEquity.go`; interactor in `internal/usecase/OmahaInteractor.go`. CLI and Web GUI (1 human vs 3–8 CPU, configurable 4/6/9-max table sizes). Derived from Texas Hold'em with key differences: 4 hole cards dealt to each player, must use exactly 2 hole cards + 3 community cards to form best 5-card hand (C(4,2)×C(5,3) = 60 combinations evaluated). Shares Hold'em's betting infrastructure: 5 CPU play styles (TAG/LAP/TAP/LAG/GTO), side pots, HUD stats, tournament mode, rebuy/addon, configurable betting limit, muck/show. Pre-flop strength evaluation adapted for 4-card hands (pair bonus, suited pairs, connectors, wrap bonus). Monte Carlo equity calculation with 4-card opponent hands. Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate, fold rate, and hesitation timing across games and adjusts CPU betting/folding strategy accordingly) - **ShortDeck (ショートデック / 6+ホールデム)**: Entities in `internal/domain/ShortDeck.go`, `internal/domain/ShortDeckPlayer.go`, `internal/domain/ShortDeckEquity.go`; interactor in `internal/usecase/ShortDeckInteractor.go`. CLI and Web GUI (1 human vs 3–8 CPU, configurable 4/6/9-max table sizes). Derived from Texas Hold'em with a 36-card deck (2–5 of each suit removed). Modified hand rankings: Flush beats Full House (harder to make with fewer cards). Lowest straight is A-6-7-8-9 (no wheel since 2–5 are removed). Shares Hold'em's betting infrastructure: 5 CPU play styles (TAG/LAP/TAP/LAG/GTO), side pots, HUD stats, tournament mode, rebuy/addon, configurable betting limit, muck/show. Config uses HoldemConfig (blinds, betting limit, tournament, table size, rebuy/addon, MetaAI). Monte Carlo equity calculation adapted for 36-card deck. Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate, fold rate, and hesitation timing across games and adjusts CPU betting/folding strategy accordingly) - **Pineapple Poker (パイナップルポーカー)**: 3枚のホールカードを配り、フロップ後に1枚をディスカードするホールデム系ポーカー。Crazy Pineapple variant。Entities in `internal/domain/Pineapple.go`, `internal/domain/PineapplePlayer.go`, `internal/domain/PineappleConfig.go`; interactor in `internal/usecase/PineappleInteractor.go`. CLI and Web GUI (1 human vs 3-8 CPU, configurable 4/6/9-max table sizes). Derived from Texas Hold'em with key difference: 3 hole cards dealt to each player, and after the flop betting round players must discard 1 hole card before the turn. Shares Hold'em's betting infrastructure: 5 CPU play styles (TAG/LAP/TAP/LAG/GTO), side pots, HUD stats, tournament mode, rebuy/addon, configurable betting limit, muck/show. Pre-flop strength evaluation adapted for 3-card hands. Monte Carlo equity calculation with 3-card opponent hands. Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate, fold rate, and hesitation timing across games and adjusts CPU betting/folding strategy accordingly). Phases: Init (0), PreFlop (1), Flop (2), Discard (3), Turn (4), River (5), Showdown (6), End (7), Rebuy (8) - **Hearts**: Entities in `internal/domain/Hearts.go`, `internal/domain/HeartsPlayer.go`; interactor in `internal/usecase/HeartsInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), trick-taking card game where players avoid taking hearts (1 point each) and Q♠ (13 points). Game ends when any player reaches 100+ points; lowest score wins. Pass 3 cards each round (left→right→across→none rotation), 2♣ leads the first trick, must follow suit, hearts can't lead until broken. Shoot the Moon: take all 26 points → 0 for you, +26 for everyone else. Optional rules: Omnibus Hearts (J♦ = -10 points). Hint: reuses Hard CPU evaluation logic to suggest optimal plays/passes. CPU AI: Easy (random), Normal (point avoidance), Hard (strategic) - **Memory (神経衰弱)**: Entities in `internal/domain/Memory.go`, `internal/domain/MemoryPlayer.go`; interactor in `internal/usecase/MemoryInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), concentration card game using a standard 52-card deck laid face-down. Each turn a player flips 2 cards: if they share the same rank the player takes the pair and gets another turn; otherwise the cards are flipped back and play passes to the next player. Game ends when all 26 pairs are taken; most pairs wins. CPU AI: Easy (random), Normal (partial memory), Hard (perfect memory) - **Klondike (ソリティア)**: Entities in `internal/domain/Klondike.go`; interactor in `internal/usecase/KlondikeInteractor.go`. CLI and Web GUI, single-player solitaire card game using a standard 52-card deck. 7 tableau columns with cascading face-down/face-up cards, a stock pile, a waste pile, and 4 foundation piles (one per suit). Build foundations from Ace to King by suit; build tableau columns in descending rank with alternating colors. Commands: reset, draw (flip stock to waste), move (waste/tableau to tableau/foundation), giveup, hint, autocomplete, log. Phases: Playing (0), GameClear (1), GameOver (2) - **FreeCell (フリーセル)**: Entities in `internal/domain/FreeCell.go`; interactor in `internal/usecase/FreeCellInteractor.go`. CLI and Web GUI, single-player solitaire card game using a standard 52-card deck. 8 tableau columns (first 4 have 7 cards, last 4 have 6 cards), all dealt face-up. 4 free cells for temporary single-card storage. 4 foundation piles: build up by suit Ace to King. Tableau: descending rank, alternating colors; only King on empty column. Supermove: max movable cards = (1 + emptyFreeCells) x 2^(emptyTableauCols). Auto-complete and undo support. Commands: reset, move (tableau/freecell/foundation), giveup, hint, autocomplete, undo, log. Phases: Playing (0), GameClear (1), GameOver (2) - **Baccarat (バカラ)**: Entities in `internal/domain/Baccarat.go`; interactor in `internal/usecase/BaccaratInteractor.go`. CLI and Web GUI, casino card game using a standard 52-card deck. Player bets on Player/Banker/Tie, then cards are dealt automatically following baccarat third-card rules. Card points: A=1, 2-9=face value, 10/J/Q/K=0. Hand value is the ones digit of the sum. Natural (8 or 9) stops drawing. Payouts: Player bet 2:1, Banker bet 1.95:1 (5% commission), Tie bet 9:1 (8:1). Uses ChipHolder for chip/betting system. Commands: reset, bet (amount + type), log. Phases: Bet (1), End (2) - **Spades (スペード)**: Entities in `internal/domain/Spades.go`, `internal/domain/SpadesPlayer.go`, `internal/domain/SpadesConfig.go`; interactor in `internal/usecase/SpadesInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), trick-taking card game with bidding. Spades are always trump. Each player bids expected tricks (0-13); bid of 0 = Nil (high risk/reward: +100 if no tricks taken, -100 otherwise). 2♣ leads the first trick, must follow suit, spades can't lead until broken. Scoring: made bid = bid×10 + overtricks (bags); failed bid = -bid×10; every 10 bags = -100 penalty. First to reach 500 wins; drop below -200 and lose. Hint: reuses Hard CPU evaluation logic to suggest optimal plays/bids. CPU AI: Easy (random), Normal (card-strength heuristic), Hard (strategic with trump management). Phases: Bid (0), Play (1), TrickEnd (2), RoundEnd (3), GameEnd (4) - **Crazy Eights (クレイジーエイト)**: Entities in `internal/domain/CrazyEights.go`, `internal/domain/CrazyEightsPlayer.go`; interactor in `internal/usecase/CrazyEightsInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), shedding card game using a standard 52-card deck. Deal 5 cards each; 1 face-up card starts the discard pile; rest form the draw pile. Play a card matching the top discard's suit or rank, or play an 8 (wild) and choose the next suit. Cannot play? Draw 1 card; if it can be played, keep turn; otherwise pass. When the draw pile is empty, recycle the discard pile (minus the top card). Round ends when a player empties their hand; scoring: 8=50 points, J/Q/K=10, A=1, others=face value. Match ends when a player reaches the point limit (default 200). Commands: reset, play, draw, suit, nextround, log - **Gin Rummy (ジンラミー)**: Entities in `internal/domain/GinRummy.go`, `internal/domain/GinRummyPlayer.go`; interactor in `internal/usecase/GinRummyInteractor.go`. CLI and Web GUI (1 human vs 1 CPU), 2-player rummy card game using a standard 52-card deck. 10 cards dealt to each player; 1 face-up card starts the discard pile; rest form the stock pile. Draw from stock or discard pile, then discard or knock. Melds are sets (3-4 cards of the same rank) and runs (3+ consecutive cards of the same suit). Knock when deadwood ≤ 10. Gin = 0 deadwood (25 bonus). Undercut = opponent has ≤ knocker's deadwood (25 bonus to opponent). First to reach point limit wins. Commands: reset, drawstock, drawdiscard, discard, knock, layoff, nextround, log - **Spider Solitaire (スパイダーソリティア)**: Entity in `internal/domain/Spider.go`, config in `internal/domain/SpiderConfig.go`; interactor in `internal/usecase/SpiderInteractor.go`. CLI and Web GUI (1人用ソリティア), 2 decks (104 cards), 10 tableau columns. First 4 columns have 6 cards (5 face-down + 1 face-up), remaining 6 columns have 5 cards (4 face-down + 1 face-up). Stock has 50 remaining cards (5 deals of 10 cards). Move same-suit descending sequences between tableau columns; any single card can go on a card one rank higher (any suit). When K-through-A same-suit sequence completes, it's automatically removed. 8 completed suits = game clear. Difficulty settings: 1 suit (easy), 2 suits (medium), 4 suits (hard). Score: start at 500, -1 per move, +100 per completed suit. Commands: reset, deal, move, giveup, hint, autocomplete, undo, log. Phases: Playing (0), GameClear (1), GameOver (2) - **Napoleon (ナポレオン)**: Entities in `internal/domain/Napoleon.go`, `internal/domain/NapoleonPlayer.go`, `internal/domain/NapoleonConfig.go`; interactor in `internal/usecase/NapoleonInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), trick-taking card game with bidding for picture cards. 52 cards + 1 Joker = 53 cards, 13 per player + 1 kitty. Bidding phase: players bid the number of picture cards (J/Q/K/A + Joker = 17 max) they expect to capture. Napoleon (highest bidder) declares trump suit and names an adjutant card (hidden ally). Hidden teams: Napoleon's Army (Napoleon + adjutant holder) vs Allied Forces. Special cards: Joker (mighty -- strongest card), Spade 3 (joker killer -- beats mighty when played). Napoleon's team must capture >= bid number of picture cards to win. CPU AI: Easy (random), Normal (basic trick strategy), Hard (strategic with trump management). Phases: Bid (0), Trump (1), Exchange (2), Play (3), TrickEnd (4), RoundEnd (5), GameEnd (6) - **Indian Poker (インディアンポーカー)**: Entities in `internal/domain/IndianPoker.go`, `internal/domain/IndianPokerPlayer.go`, `internal/domain/IndianPokerConfig.go`, `internal/domain/IndianPokerHumanProfile.go`; interactor in `internal/usecase/IndianPokerInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), Indian Poker where each player can see everyone else's card but not their own. Uses betting system (fold/check/call/bet/raise/all-in) with side pot support. 3 CPU play styles (TAG/LAP/TAP) reusing HoldemPlayStyle. Configurable ante, initial chips, betting limit (Fixed/Pot Limit/No Limit). Meta-AI (session-scoped adaptive CPU behavior -- tracks the human player's bluff rate, fold rate, and hesitation timing across games and adjusts CPU betting/folding strategy accordingly). Phases: Init (0), Ante (1), Betting (2), Showdown (3), End (4) - **Video Poker (ビデオポーカー)**: Entity in `internal/domain/VideoPoker.go`; interactor in `internal/usecase/VideoPokerInteractor.go`. CLI and Web GUI, single-player casino card game using a standard 52-card deck. Jacks or Better payout variant with 1-5 coin bet system. Deal 5 cards, hold any number of cards, draw replacements, evaluate final hand. Reuses `evalFiveCardHand` from `hand_eval.go` for hand evaluation. Qualifying hands: Jacks or Better (pair of J/Q/K/A). Payouts scale with bet amount (1-5 coins); Royal Flush with max bet pays 4000 coins. Uses ChipHolder for chip/betting system. Commands: reset, bet, hold, log. Phases: Bet (0), Draw (1), Result (2) - **Deuces Wild (デューシーズワイルド)**: Variant of Video Poker using `VideoPokerVariantConfig` strategy pattern. CLI and Web GUI, single-player casino card game using a standard 52-card deck. All 2s are wild cards. Pay table: Natural Royal Flush 250x (800@5 coins), Four Deuces 200x, Wild Royal Flush 25x, Five of a Kind 15x, Straight Flush 9x, Four of a Kind 5x, Full House 3x, Flush 2x, Straight 2x, Three of a Kind 1x. Reuses VideoPoker domain with variant-specific hand evaluation and pay table. Commands: reset, bet, hold, log. Phases: Bet (0), Draw (1), Result (2) - **Joker Poker (ジョーカーポーカー)**: Variant of Video Poker using `VideoPokerVariantConfig` strategy pattern. CLI and Web GUI, single-player casino card game using a 53-card deck (standard 52 + 1 joker). Joker is wild. Pay table: Natural Royal Flush 250x (800@5 coins), Five of a Kind 200x, Wild Royal Flush 100x, Straight Flush 50x, Four of a Kind 20x, Full House 7x, Flush 5x, Straight 3x, Three of a Kind 2x, Two Pair 1x, Kings or Better 1x. Reuses VideoPoker domain with variant-specific hand evaluation and pay table. Commands: reset, bet, hold, log. Phases: Bet (0), Draw (1), Result (2) - **Euchre (ユーカー)**: Entities in `internal/domain/Euchre.go`, `internal/domain/EuchrePlayer.go`, `internal/domain/EuchreConfig.go`; interactor in `internal/usecase/EuchreInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), 4-player 2-team (0+2 vs 1+3) trick-taking card game with a 24-card deck (9-A of each suit). Features Right Bower (Jack of trump suit) and Left Bower (Jack of same-color suit) as highest trump cards. Trump suit determined via bidding: dealer turns up a card, players decide whether to order it up or pass; if all pass, players can call a different suit. "Going alone" option for bonus points (partner sits out). Scoring: Makers 3-4 tricks = +1, March (5 tricks) = +2, Euchred (defenders win) = defenders +2, Going alone march = +4, Going alone 3-4 tricks = +1. First team to 10 points wins. CPU AI: Easy (random), Normal (basic trick strategy), Hard (strategic with trump management). Phases: PickUp (0), CallTrump (1), Discard (2), Play (3), TrickEnd (4), RoundEnd (5), GameEnd (6) - **Pyramid (ピラミッド)**: Entity in `internal/domain/Pyramid.go`; interactor in `internal/usecase/PyramidInteractor.go`. CLI and Web GUI, single-player solitaire card game using a standard 52-card deck. 28 cards dealt face-up in a 7-row pyramid (row i has i+1 cards). Remaining 24 cards form the stock pile; waste pile starts empty. Remove exposed cards that sum to 13 (A=1, 2-10=face value, J=11, Q=12, K=13 removed alone). A card is "exposed" when both cards below it (in the next row) are removed. Draw from stock to waste; waste top card can pair with exposed pyramid cards. Game clear when all 28 pyramid cards are removed. Undo and hint support. Commands: reset, draw, remove (pair/king/waste), giveup, hint, undo, log. Phases: Playing (0), GameClear (1), GameOver (2) - **TriPeaks (トリピークス)**: Entity in `internal/domain/TriPeaks.go`; interactor in `internal/usecase/TriPeaksInteractor.go`. CLI and Web GUI, single-player solitaire card game using a standard 52-card deck. Three overlapping pyramids (peaks) form a 28-card tableau. Remove exposed cards that are ±1 rank from the waste pile top card (K-A wrap). Win by removing all 28 tableau cards. Undo and hint support. Commands: reset, draw, remove, giveup, hint, undo, log. Phases: Playing (0), GameClear (1), GameOver (2) - **Cribbage (クリベッジ)**: Entities in `internal/domain/Cribbage.go`, `internal/domain/CribbagePlayer.go`, `internal/domain/CribbageConfig.go`, `internal/domain/cribbage_scoring.go`; interactor in `internal/usecase/CribbageInteractor.go`. CLI and Web GUI (1 human vs 1 CPU), classic 2-player pegging and scoring card game using a standard 52-card deck. 6 cards dealt to each player; each discards 2 to the crib. Starter card cut from deck. Pegging phase: alternate playing cards aiming for 31 (points for 15, pairs, runs, last card). Show phase: score hands and crib (fifteens, pairs, runs, flush, nobs). First to reach point limit (default 121) wins. CPU AI: Easy (random), Normal (basic strategy), Hard (strategic with optimal discarding and pegging). Phases: Discard (0), Cut (1), Pegging (2), Show (3), RoundEnd (4), GameEnd (5) - **Three Card Poker (スリーカードポーカー)**: Entity in `internal/domain/ThreeCard.go`; interactor in `internal/usecase/ThreeCardInteractor.go`. CLI and Web GUI, single-player casino card game using a standard 52-card deck. Player places an Ante bet and optional Pair Plus side bet, then receives 3 cards. Player decides to Play (place a Play bet equal to Ante) or Fold (forfeit Ante). Dealer must have Q-high or better to qualify. If dealer doesn't qualify, Ante pays 1:1 and Play bet pushes. If dealer qualifies, higher hand wins. Ante Bonus pays for premium hands regardless of dealer result (Straight Flush 5:1, Three of a Kind 4:1, Straight 1:1). Pair Plus pays independently (Straight Flush 40:1, Three of a Kind 30:1, Straight 6:1, Flush 3:1, Pair 1:1). Hand rankings: Straight Flush > Three of a Kind > Straight > Flush > Pair > High Card. Uses ChipHolder for chip/betting system. Commands: reset, bet, play, fold, log. Phases: Bet (1), Action (2), End (3) - **Oh Hell (オー・ヘル)**: Entities in `internal/domain/OhHell.go`, `internal/domain/OhHellPlayer.go`, `internal/domain/OhHellConfig.go`; interactor in `internal/usecase/OhHellInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), trick-taking card game with variable hand sizes per round. Each round the hand size changes (decreasing then increasing, configurable), and the trump suit is determined by the top card of the remaining deck. Players bid the exact number of tricks they expect to win, with a dealer restriction ("hook" rule: the dealer's bid cannot make the total bids equal the number of tricks available). Scoring: exact bid match = 10 + bid points, miss = 0 (standard) or penalty variant (-|difference|). Game ends after all rounds are played; highest cumulative score wins. Configurable options: CPU difficulty (Easy/Normal/Hard), max hand size (1-13, default 10), scoring variant (Standard/Penalty), round direction (DownOnly/DownAndUp). Hint: suggests optimal bids and plays. CPU AI: Easy (random), Normal (basic strategy), Hard (strategic). Phases: Bid (0), Play (1), TrickEnd (2), RoundEnd (3), GameEnd (4) - **Contract Bridge (コントラクトブリッジ)**: Entities in `internal/domain/Bridge.go`, `internal/domain/BridgePlayer.go`, `internal/domain/BridgeConfig.go`; interactor in `internal/usecase/BridgeInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), 4-player 2-team (0+2 vs 1+3) trick-taking card game with a standard 52-card deck (13 cards per player). Features an auction (bidding) phase where players bid to set the contract (level 1-7, suit or NoTrump), with Pass/Double/Redouble options. The winning bidder becomes Declarer, their partner becomes Dummy (hand revealed and played by Declarer). Trick play follows suit rules; trump suit (if any) wins. Rubber bridge scoring: contract points go below the line (100 below = game won), overtricks/undertricks/bonuses above the line. First team to win 2 games wins the rubber (with rubber bonus 500/700). Vulnerability affects penalty/bonus scoring. CPU AI: Easy (random), Normal (basic trick strategy), Hard (strategic with trump management and auction evaluation). Phases: Bid (0), Play (1), TrickEnd (2), RoundEnd (3), GameEnd (4) - **Speed (スピード)**: Entities in `internal/domain/Speed.go`, `internal/domain/SpeedPlayer.go`, `internal/domain/SpeedConfig.go`; interactor in `internal/usecase/SpeedInteractor.go`. CLI and Web GUI (1 human vs 1 CPU), 2-player speed card game using a standard 52-card deck. Each player has a hand pile and a draw pile. Two center piles are shared. Players simultaneously play cards ±1 rank from the top of a center pile. When both players are stuck, they flip new cards onto the center piles. First player to empty all cards wins. Commands: reset, play (cardIndex, pileIndex), flip, hint, log. Phases: Play (0), Stuck (1), GameEnd (2) - **Go Fish (ゴーフィッシュ)**: Entities in `internal/domain/GoFish.go`, `internal/domain/GoFishPlayer.go`, `internal/domain/GoFishConfig.go`; interactor in `internal/usecase/GoFishInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), 4-player card game using a standard 52-card deck. 5 cards dealt to each player. On your turn, ask any opponent for a specific rank you hold; if they have it, they give all matching cards; otherwise "Go Fish" -- draw from the stock pile. Collect all 4 cards of the same rank to form a "book". Game ends when all 13 books are formed or the stock pile is empty and no player can act. Most books wins. CPU AI: Easy (random), Normal (memory-assisted targeting), Hard (full history analysis for optimal target/rank selection). Meta-AI (session-scoped adaptive CPU behavior). Phases: Play (0), GameEnd (1) - **Canasta (カナスタ)**: Entities in `internal/domain/Canasta.go`, `internal/domain/CanastaPlayer.go`, `internal/domain/CanastaConfig.go`; interactor in `internal/usecase/CanastaInteractor.go`. CLI and Web GUI (1 human vs 1 CPU), 2-player rummy card game using 2 decks + 4 jokers (108 cards). 15 cards dealt to each player. Wild cards: all 2s and Jokers. Red 3s are automatically laid down for bonus points. Melds are sets of 3+ same-rank cards (min 2 natural, max 3 wilds). Canasta = 7+ card meld (natural canasta 500 bonus, mixed canasta 300 bonus). Discard pile pickup requires a natural pair matching the top card; pile freezes when a wild card is discarded on it. Initial meld minimum based on cumulative score (15/50/90/120). Going out requires at least 1 canasta (100 bonus, concealed 200 bonus). First to reach point limit (default 5000) wins. CPU AI: Easy (random), Normal (basic strategy), Hard (strategic pile management). Commands: reset, drawstock, drawdiscard, meld, skipmeld, discard, goout, nextround, log. Phases: Draw (0), Meld (1), Discard (2), RoundEnd (3), GameEnd (4) - **Pinochle (ピノクル)**: Entities in `internal/domain/Pinochle.go`, `internal/domain/PinochlePlayer.go`, `internal/domain/PinochleConfig.go`; interactor in `internal/usecase/PinochleInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), 4-player 2-team (0+2 vs 1+3) trick-taking card game with bidding and melding using a 48-card double deck (9,10,J,Q,K,A × 2 per suit). 12 cards dealt to each player. Bidding phase: players bid expected total points (min 20, increments of 1). Highest bidder declares trump suit. Meld phase: all players display melds (card combinations) for points. Meld types include Dix (10), Marriage (20/40), Pinochle (40), Around (40-100), Run (150), and double variants (300-1500). Play phase: 12 tricks with must-follow-suit, must-trump-if-void, must-win-if-possible rules. Card rank: A > 10 > K > Q > J > 9. Card point values: A=11, 10=10, K=4, Q=3, J=2, 9=0. Last trick bonus 10 points. Scoring: bidding team keeps points if total >= bid, otherwise loses bid amount; defending team always keeps points. Melds forfeited if team wins 0 tricks. First to reach point limit (default 1500) wins. CPU AI: Easy (random), Normal (meld-aware bidding), Hard (strategic with trump management). Commands: reset, bid, pass, trump, meld, play, next, nextround, hint, log. Phases: Bid (0), Trump (1), Meld (2), Play (3), TrickEnd (4), RoundEnd (5), GameEnd (6) - **Golf Solitaire (ゴルフ)**: Entity in `internal/domain/Golf.go`; interactor in `internal/usecase/GolfInteractor.go`. CLI and Web GUI, single-player solitaire card game using a standard 52-card deck. 7 columns of 5 face-up cards (35 total) form the tableau. Only the bottom-most non-removed card per column is exposed. Remove exposed cards that are ±1 rank from the waste pile top card (K-A wrap). Win by removing all 35 tableau cards. Undo and hint support. Commands: reset, draw, remove, giveup, hint, undo, log. Phases: Playing (0), GameClear (1), GameOver (2) - **Pig's Tail (ぶたのしっぽ)**: Entity in `internal/domain/PigsTail.go`, `internal/domain/PigsTailPlayer.go`; interactor in `internal/usecase/PigsTailInteractor.go`. CLI and Web GUI (1 human vs 3 CPU), matching card game using a standard 52-card deck. Cards are placed face-down in a circle pile. Players take turns drawing 1 card and placing it face-up on the center pile. If the drawn card's suit matches the center top card's suit, the player takes all center cards as a penalty. When the circle pile is empty, the player with the most cards in hand loses. Commands: reset, draw, log. Phases: Play (0), GameEnd (1) - **Clock Solitaire (クロックソリティア)**: Entity in `internal/domain/ClockSolitaire.go`; interactor in `internal/usecase/ClockSolitaireInteractor.go`. CLI and Web GUI, single-player solitaire card game using a standard 52-card deck. 52 cards are dealt face-down into 13 piles of 4 cards: 12 piles placed at clock positions 1–12 and 1 pile in the center (King). The game proceeds automatically — one card at a time is flipped face-up from the current pile and placed at the bottom of the pile matching its rank (A=1, 2–10=face value, J=11, Q=12, K=center). The game clears when all cards are flipped face-up before the 4th King is placed (i.e., the center pile has fewer than 4 face-up cards). The game is over when the 4th King is turned over while face-down cards remain. Fully automatic: no player choices after the initial shuffle. Commands: reset, step (advance one card), autoplay (play to completion), log. Phases: Playing (0), GameClear (1), GameOver (2) - **Seven Card Stud (セブンカード・スタッド)**: Entities in `internal/domain/SevenCardStud.go`, `internal/domain/SevenCardStudPlayer.go`, `internal/domain/SevenCardStudConfig.go`; interactor in `internal/usecase/SevenCardStudInteractor.go`. CLI and Web GUI (1 human vs 1-6 CPU, configurable 2-7 table sizes). Classic stud poker with no community cards — each player receives 2 face-down hole cards and 1 face-up door card on Third Street, then 1 face-up card each on Fourth through Sixth Street, and 1 final face-down card on Seventh Street. Uses antes and bring-in (lowest door card) instead of blinds. Betting order changes each street based on best visible hand (door cards). Best 5 out of 7 cards wins. 5 CPU play styles (TAG/LAP/TAP/LAG/GTO), side pots, HUD stats (VPIP%/PFR%/3Bet%/AF), configurable betting limit (Fixed/Pot Limit/No Limit), tournament mode with ante escalation, rebuy/addon, muck/show, Meta-AI. ## Action Log (棋譜) All games support a `log` command that returns a cumulative action log of every action taken during the current game session. The log reveals all hidden cards (e.g., face-down cards, opponent hands) so players can review the full history of the game after the fact. Each log entry contains the turn number, player index, action type, a human-readable detail string, and optionally the cards involved in the action. # New Game Addition Checklist When adding a new game, follow this checklist to avoid post-feat fix commits. Complete ALL items before creating the PR. ## Backend (Go) 1. **Domain**: Create `internal/domain/.go`, `Player.go`, `Config.go` (if configurable) 2. **Reuse shared helpers**: `deal_helper.go` (dealAllCards), `hand_eval.go` (hand evaluation), `betting.go` (chip/betting), `play_style_helper.go` (CPU styles), `player_helpers.go` (resetPlayers), `hesitation.go` (CPU delay), `memory_manager.go`/`memory_decay.go` (CPU memory AI), `GamePlayer.go` (base player struct), `ChipHolder.go` (chip system), `kicker.go` (kicker comparison) 3. **Interactor**: `internal/usecase/Interactor.go` with presenter interface in `internal/usecase/presenter/` 4. **Controller**: CUI controller in `internal/adapter/controller/`, Web controller in `internal/adapter/controller/`, reuse `cuiutil` package for input parsing and `ClampIntPtr` for config validation 5. **Presenter**: CUI and Web presenters in `internal/adapter/presenter/`, reuse `buildCuiOutput`, `cuiCardListStr`, `ActionLogOutput` helpers, `WebOutputBase` for common web output fields 6. **Infrastructure**: Register in `cmd/trumpcards/main.go` (CLI) and `internal/infrastructure/web/TrumpCardsWeb.go` (API route) 6b. **Cloudflare Worker (WASM)**: Register in the appropriate `cmd/workers/{casino,classic,solo}/main.go` using `registerKV`. Worker assignment: casino (table/poker games), classic (trick-taking/matching), solo (solitaire/rummy). Also verify `frontend/src/api/gameApi.ts` `workerUrl` mapping matches. 7. **Run `goimports -w` and `golangci-lint run ./...`** on all new files 8. **80%+ branch coverage** for all new packages ## Frontend (React) 9. **Page**: `frontend/src/pages/Page.tsx` with test file, reuse `useGamePageSetup` hook, `usePhaseNames`, `gameReplay`, `useCardDimensions`, `gameExec` API helper 10. **Shared components**: Use `PhaseIndicator`, `SettingsPanel`, `ConfirmDialog`, `ActionLogSection`, `GameFooter`, `GameMessageBox`, `AnimatedCardBack`, `ErrorBoundary` 11. **i18n**: Add `frontend/src/i18n/locales/{ja,en}/.json` translation files 12. **Router**: Add route in `frontend/src/App.tsx` and NavBar entry 13. **Run `bun run build && bun run check && bun run test`** ## Documentation (same commit) 14. **`README.md`**: Add game description and CLI command 15. **`CLAUDE.md`**: Add game name to available games list in Commands section 16. **`docs/games.md`**: Add game entity description 17. **`docs/architecture.md`**: Update endpoint count and list 18. **`api/openapi.yaml`**: Add endpoint path, tag definition, and request/response schemas in components 19. **`docs/manual/cui/.md`** and **`docs/manual/web/.md`**: Add game manuals 20. **`frontend/src/constants/manualTexts.ts`**: Import the web manual and add route mapping entry ## Final verification 21. `go test -tags test ./...` -- all tests pass 22. `golangci-lint run ./...` -- no warnings 23. `cd frontend && bun run build && bun run check && bun run test` -- all pass 24. **E2E test**: Create `frontend/e2e/.spec.ts` with basic game flow test 25. `cd frontend && bun run e2e` -- all E2E tests pass import type { ActionLogResponse, BaccaratResponse, BlackJackResponse, BridgeResponse, CanastaResponse, ClockSolitaireResponse, CrazyEightsResponse, CribbageResponse, DaifugoConfigInput, DaifugoResponse, DoubtConfig, DoubtResponse, EuchreResponse, FreeCellResponse, GinRummyResponse, GoFishResponse, GolfResponse, HeartsResponse, HoldemResponse, IndianPokerResponse, KlondikeResponse, MemoryResponse, NapoleonResponse, OhHellResponse, OldMaidResponse, OmahaResponse, PigsTailResponse, PineappleResponse, PinochleResponse, PokerResponse, PyramidResponse, SevenCardStudResponse, SevensResponse, ShortDeckResponse, SpadesResponse, SpeedConfig, SpeedResponse, SpiderResponse, ThreeCardResponse, TriPeaksResponse, VideoPokerResponse, } from '../types/card'; /** Unique session identifier for correlating API requests. */ ⋮---- /** Worker base URLs for Cloudflare deployment. Empty strings for Docker (relative URLs). */ ⋮---- /** Maps each game to its Worker base URL. */ ⋮---- async function postJson(url: string, body: unknown): Promise function gameExec(game: string, body: Record): Promise /** Configuration options for BlackJack game settings. */ export interface BlackJackConfigInput { dealerHitsSoft17?: boolean; cpuPlayerCount?: number; countingEnabled?: boolean; doubleAfterSplit?: boolean; countingSystem?: number; deckPenetration?: number; surrenderRule?: number; } /** Side bet and multi-hand options for BlackJack. */ export interface BlackJackBetOptions { perfectPairsBet?: number; twentyOnePlus3Bet?: number; handCount?: number; } /** API client for the BlackJack /blackjack/exec endpoint. */ ⋮---- /** Configuration options for Poker game settings. */ export interface PokerConfigInput { cpuCount?: number; jokerCount?: number; bettingLimit?: number; isLowball?: boolean; cpuMetaAI?: boolean; } /** API client for the Poker /poker/exec endpoint. */ ⋮---- /** API client for the Old Maid /oldmaid/exec endpoint. */ ⋮---- /** API client for the Daifugo /daifugo/exec endpoint. */ ⋮---- /** API client for the Doubt /doubt/exec endpoint. */ ⋮---- /** Configuration options for Sevens game settings. */ export interface SevensConfigInput { tunnelEnabled?: boolean; tunnelSkipWidth?: number; jokerCount?: number; cpuStrategy?: number; maxPasses?: number; noJokerFinish?: boolean; jokerReclaim?: boolean; endStop?: boolean; jokerConsecutiveBanned?: boolean; } /** API client for the Sevens /sevens/exec endpoint. */ ⋮---- /** Configuration options for Texas Hold'em game settings. */ export interface HoldemConfigInput { smallBlind?: number; bigBlind?: number; tournamentMode?: boolean; blindLevelHands?: number; blindMultiplier?: number; bettingLimit?: number; tableSize?: number; rebuyEnabled?: boolean; rebuyMaxCount?: number; rebuyChips?: number; rebuyPeriodHands?: number; addonEnabled?: boolean; addonChips?: number; addonAfterHand?: number; cpuMetaAI?: boolean; } /** Command set shared by Hold'em-family games. */ type HoldemLikeCommand = | 'reset' | 'fold' | 'check' | 'call' | 'bet' | 'raise' | 'allin' | 'rebuy' | 'skiprebuy' | 'addon' | 'skipaddon' | 'muck' | 'show'; /** Factory for Hold'em-family APIs that share the same exec pattern. */ function createHoldemLikeApi(game: string) /** API client for the Texas Hold'em /holdem/exec endpoint. */ ⋮---- /** API client for the Omaha Hold'em /omaha/exec endpoint. */ ⋮---- /** API client for the Short Deck Hold'em /shortdeck/exec endpoint. */ ⋮---- /** Configuration options for Seven Card Stud game settings. */ export interface SevenCardStudConfigInput { ante?: number; bringIn?: number; smallBet?: number; bigBet?: number; tournamentMode?: boolean; anteLevelHands?: number; anteMultiplier?: number; bettingLimit?: number; tableSize?: number; rebuyEnabled?: boolean; rebuyMaxCount?: number; rebuyChips?: number; rebuyPeriodHands?: number; addonEnabled?: boolean; addonChips?: number; addonAfterHand?: number; cpuMetaAI?: boolean; } /** API client for the Seven Card Stud /sevencardstud/exec endpoint. */ ⋮---- /** Configuration options for Pineapple Poker (extends Hold'em with cardIdx for discard). */ export interface PineappleConfigInput extends HoldemConfigInput { cardIdx?: number; } /** API client for the Pineapple Poker /pineapple/exec endpoint. */ ⋮---- /** Configuration options for Hearts game settings. */ export interface HeartsConfigInput { cpuDifficulty?: number; pointLimit?: number; omnibusJD?: boolean; } /** API client for the Hearts /hearts/exec endpoint. */ ⋮---- /** Configuration options for Spades game settings. */ export interface SpadesConfigInput { cpuDifficulty?: number; pointLimit?: number; nilBonus?: number; bagPenaltyThreshold?: number; } /** Factory for bid-play trick-taking APIs that share the same exec pattern. */ function createBidPlayApi(game: string) /** API client for the Spades /spades/exec endpoint. */ ⋮---- /** Configuration options for Oh Hell game settings. */ export interface OhHellConfigInput { cpuDifficulty?: number; maxHandSize?: number; scoringVariant?: number; roundDirection?: number; } /** API client for the Oh Hell /ohhell/exec endpoint. */ ⋮---- /** Configuration options for Memory game settings. */ export interface MemoryConfigInput { cpuDifficulty?: number; } /** API client for the Memory /memory/exec endpoint. */ ⋮---- /** Source or target zone for a Klondike card move. */ export interface KlondikeMoveZone { zone: string; col?: number; cardIndex?: number; } /** Configuration options for Klondike game settings. */ export interface KlondikeConfigInput { drawCount?: number; scoringMode?: number; } /** API client for the Klondike /klondike/exec endpoint. */ ⋮---- /** Source or target zone for a FreeCell card move. */ export interface FreeCellMoveZone { zone: string; col?: number; cell?: number; cardIndex?: number; } /** API client for the FreeCell /freecell/exec endpoint. */ ⋮---- /** Configuration options for Crazy Eights game settings. */ export interface CrazyEightsConfigInput { cpuDifficulty?: number; pointLimit?: number; } /** API client for the Crazy Eights /crazyeights/exec endpoint. */ ⋮---- /** Configuration options for Gin Rummy game settings. */ export interface GinRummyConfigInput { cpuDifficulty?: number; pointLimit?: number; } /** API client for the Gin Rummy /ginrummy/exec endpoint. */ ⋮---- /** Configuration options for Canasta game settings. */ export interface CanastaConfigInput { cpuDifficulty?: number; pointLimit?: number; } /** API client for the Canasta /canasta/exec endpoint. */ ⋮---- /** Configuration options for Pinochle game settings. */ export interface PinochleConfigInput { cpuDifficulty?: number; pointLimit?: number; } /** API client for the Pinochle /pinochle/exec endpoint. */ ⋮---- /** Configuration options for Cribbage game settings. */ export interface CribbageConfigInput { cpuDifficulty?: number; pointLimit?: number; } /** API client for the Cribbage /cribbage/exec endpoint. */ ⋮---- /** API client for the Baccarat /baccarat/exec endpoint. */ ⋮---- /** API client for the Three Card Poker /threecard/exec endpoint. */ ⋮---- /** Source or target zone for a Spider card move. */ export interface SpiderMoveZone { zone: string; col?: number; cardIndex?: number; } /** Configuration options for Spider game settings. */ export interface SpiderConfigInput { difficulty?: number; } /** API client for the Spider /spider/exec endpoint. */ ⋮---- /** Configuration options for Napoleon game settings. */ export interface NapoleonConfigInput { cpuDifficulty?: number; minBid?: number; pointLimit?: number; } /** API client for the Napoleon /napoleon/exec endpoint. */ ⋮---- /** Configuration options for Indian Poker game settings. */ export interface IndianPokerConfigInput { ante?: number; bettingLimit?: number; cpuMetaAI?: boolean; } /** API client for the Indian Poker /indianpoker/exec endpoint. */ ⋮---- /** Configuration options for Bridge game settings. */ export interface BridgeConfigInput { cpuDifficulty?: number; } /** API client for the Bridge /bridge/exec endpoint. */ ⋮---- /** Configuration options for Euchre game settings. */ export interface EuchreConfigInput { cpuDifficulty?: number; pointLimit?: number; } /** API client for the Euchre /euchre/exec endpoint. */ ⋮---- /** Source card for a Pyramid remove action. */ export interface PyramidRemoveCard { zone: string; row?: number; col?: number; } /** API client for the Pyramid /pyramid/exec endpoint. */ ⋮---- /** API client for the TriPeaks /tripeaks/exec endpoint. */ ⋮---- /** Factory for video poker variant APIs that share the same exec pattern. */ function createVideoPokerApi(game: string) /** API client for the Video Poker /videopoker/exec endpoint. */ ⋮---- /** API client for the Deuces Wild /deuceswild/exec endpoint. */ ⋮---- /** API client for the Joker Poker /jokerpoker/exec endpoint. */ ⋮---- /** API client for the Speed /speed/exec endpoint. */ ⋮---- /** Configuration options for Go Fish game settings. */ export interface GoFishConfigInput { cpuDifficulty?: number; } /** API client for the Go Fish /gofish/exec endpoint. */ ⋮---- /** API client for the Golf Solitaire /golf/exec endpoint. */ ⋮---- /** Pig's Tail game API client. */ ⋮---- /** API client for the Clock Solitaire /clocksolitaire/exec endpoint. */ ⋮---- type Game = (typeof games)[number]; /** API clients for fetching action logs from each game's /log endpoint. */ import { useTranslation } from 'react-i18next'; import { btnDanger, btnPrimary, btnSuccess, btnWarning } from '../../styles/buttonStyles'; import { BJ_SUGGEST_DOUBLE, BJ_SUGGEST_DOUBLE_STAND, BJ_SUGGEST_HIT, BJ_SUGGEST_SPLIT, BJ_SUGGEST_STAND, BJ_SUGGEST_SURRENDER, highlightClass, } from './bjConstants'; /** Props for BlackJack action phase control buttons. */ export interface BjActionPhaseControlsProps { loading: boolean; hintEnabled: boolean; suggestedAction: number; showDoubleDown: boolean; showSplit: boolean; showSurrender: boolean; onHit: () => void; onStand: () => void; onDoubleDown: () => void; onSplit: () => void; onSurrender: () => void; } /** Renders BlackJack action phase buttons (hit, stand, double, split, surrender). */ import { useTranslation } from 'react-i18next'; import { btnPrimary, btnSuccess, btnWarning } from '../../styles/buttonStyles'; import { BJ_COUNTING_HILO, BJ_COUNTING_KO, BJ_COUNTING_OMEGA2, BJ_COUNTING_ZEN, BJ_VALID_PENETRATIONS, } from './bjConstants'; ⋮---- /** Props for BlackJack bet phase controls. */ export interface BjBetPhaseControlsProps { betAmount: number; onBetAmountChange: (v: number) => void; deckCount: number; onDeckCountChange: (v: number) => void; cpuPlayerCount: number; onCpuPlayerCountChange: (v: number) => void; hintEnabled: boolean; onToggleHint: () => void; dealerHitsSoft17: boolean; onToggleSoft17: () => void; countingEnabled: boolean; onToggleCounting: () => void; doubleAfterSplit: boolean; onToggleDAS: () => void; countingSystem: number; onCountingSystemChange: (v: number) => void; deckPenetration: number; onDeckPenetrationChange: (v: number) => void; handCount: number; onHandCountChange: (v: number) => void; surrenderRule: number; onSurrenderRuleChange: (v: number) => void; loading: boolean; onBet: () => void; perfectPairsBet: number; onPerfectPairsBetChange: (v: number) => void; twentyOnePlus3Bet: number; onTwentyOnePlus3BetChange: (v: number) => void; /** When true, the advanced settings section is expanded by default (e.g. on desktop). */ autoExpandAdvanced?: boolean; } ⋮---- /** When true, the advanced settings section is expanded by default (e.g. on desktop). */ ⋮---- /** Renders BlackJack bet phase controls with basic settings and collapsible advanced options. */ ⋮---- {/* Basic settings: bet amount, hand count */} ⋮---- {/* Advanced settings: collapsible */} ⋮---- {/* Side bets */} ⋮---- {/* Deck & CPU count */} ⋮---- {/* Toggle buttons & selects */} ⋮---- title= /** Hi-Lo counting system constant. Must match domain `BJCounting` value. */ ⋮---- /** KO counting system constant. Must match domain `BJCounting` value. */ ⋮---- /** Zen counting system constant. Must match domain `BJCounting` value. */ ⋮---- /** Omega II counting system constant. Must match domain `BJCounting` value. */ ⋮---- /** Late surrender rule constant. Must match domain `BJSurrender` value. */ ⋮---- /** Early surrender rule constant. Must match domain `BJSurrender` value. */ ⋮---- /** No surrender rule constant. Must match domain `BJSurrender` value. */ ⋮---- /** Valid deck penetration percentage options. Must match domain `BJPenetration` values. */ ⋮---- /** Perfect Pairs side bet type constant. Must match domain `BJSideBet` value. */ ⋮---- /** Suggested action: none. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: hit. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: stand. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: double down. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: split. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: surrender. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: decline insurance. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Suggested action: double if allowed, otherwise stand. Must match domain `BJSuggestedAction` value. */ ⋮---- /** Return a CSS class with highlight ring when the action is suggested. */ export function highlightClass(base: string, isHighlighted: boolean): string import { useTranslation } from 'react-i18next'; import { btnDanger, btnPrimary } from '../../styles/buttonStyles'; import { BJ_SUGGEST_STAND, BJ_SUGGEST_SURRENDER, highlightClass } from './bjConstants'; /** Props for BlackJack early surrender phase controls. */ export interface BjEarlySurrenderPhaseControlsProps { loading: boolean; hintEnabled: boolean; suggestedAction: number; onSurrender: () => void; onContinue: () => void; } /** Renders early surrender and continue buttons for BlackJack. */ ⋮---- className= import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { btnOutline } from '../../styles/buttonStyles'; /** Props for BlackJack end phase controls. */ export interface BjEndPhaseControlsProps { loading: boolean; onReset: () => void; onManualReset?: () => void; autoAdvanceSeconds?: number; } /** Renders the reset button with optional auto-advance countdown for BlackJack end phase. */ export function BjEndPhaseControls(props: BjEndPhaseControlsProps) import { useTranslation } from 'react-i18next'; import { btnDanger, btnWarning } from '../../styles/buttonStyles'; import { BJ_SUGGEST_DECLINE_INSURANCE, highlightClass } from './bjConstants'; /** Props for BlackJack insurance phase controls. */ export interface BjInsurancePhaseControlsProps { loading: boolean; hintEnabled: boolean; suggestedAction: number; onInsurance: () => void; onDecline: () => void; } /** Renders insurance and decline buttons for BlackJack insurance phase. */ export function BjInsurancePhaseControls(props: BjInsurancePhaseControlsProps) ⋮---- import { useTranslation } from 'react-i18next'; /** Props for BlackJack hand status badges. */ export interface HandStatusBadgesProps { busted: boolean; doubled: boolean; isBlackJack: boolean; surrendered: boolean; } /** Renders status badges (bust, double-down, blackjack, surrender) for a hand. */ ⋮---- import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import type { CliLogEntry } from '../../utils/cli/types'; ⋮---- /** Props for the CliTerminal component. */ interface CliTerminalProps { /** Log entries to display. */ logEntries: CliLogEntry[]; /** Callback when the user submits a command. */ onCommand: (command: string) => void; /** Whether the input is disabled (e.g., while loading). */ disabled: boolean; } ⋮---- /** Log entries to display. */ ⋮---- /** Callback when the user submits a command. */ ⋮---- /** Whether the input is disabled (e.g., while loading). */ ⋮---- /** Renders a pseudo-terminal UI with log display and command input. */ ⋮---- // Auto-scroll to bottom when new entries arrive // biome-ignore lint/correctness/useExhaustiveDependencies: scroll must trigger on entry count change ⋮---- // Focus input on mount ⋮---- // Add to history ⋮---- {/* Log display area */} ⋮---- {/* Command input */} import { useTranslation } from 'react-i18next'; import { focusRingWhite } from '../../styles/buttonStyles'; /** SVG icon representing a terminal/CLI. */ ⋮---- /** SVG icon representing a graphical UI. */ ⋮---- /** Props for the CliToggle component. */ interface CliToggleProps { /** Whether CLI mode is currently enabled. */ cliEnabled: boolean; /** Callback to toggle CLI mode. */ onToggle: () => void; } ⋮---- /** Whether CLI mode is currently enabled. */ ⋮---- /** Callback to toggle CLI mode. */ ⋮---- /** Renders a toggle button to switch between GUI and CLI mode. */ /** A single setting item (checkbox or select) in the settings panel. */ export interface SettingsItem { type: 'checkbox' | 'select'; id: string; label: string; tooltip?: string; // checkbox checked?: boolean; onToggle?: (checked: boolean) => void; // select value?: string | number; options?: { value: string | number; label: string }[]; onSelect?: (value: string) => void; disabled?: boolean; } ⋮---- // checkbox ⋮---- // select ⋮---- /** A group of related settings items with optional title. */ export interface SettingsGroup { id?: string; title?: string; items: SettingsItem[]; } interface SettingsPanelProps { title: string; groups: SettingsGroup[]; } /** Renders a collapsible settings panel with grouped checkboxes and selects. */ ⋮----
import { useTranslation } from 'react-i18next'; import { playerAreaBase } from '../../styles/gameStyles'; import type { DaifugoPlayerData } from '../../types/card'; import { CpuTurnArea } from '../CpuTurnArea'; import { StatusBadge } from '../StatusBadge'; /** CSS class for Daifugo player area layout. */ ⋮---- /** Renders a CPU player area for Daifugo with card count and rank. */ export function DaifugoCpuArea( ⋮---- import { useTranslation } from 'react-i18next'; import type { DaifugoExchangeAction } from '../../types/card'; import { cardLabel } from '../../utils/cardUtils'; import { findPlayerName } from '../../utils/playerUtils'; /** Renders the card exchange log between ranked players in Daifugo. */ export function DaifugoExchangeLog({ players, actions, }: { players: { id: number; isHuman: boolean }[]; actions: DaifugoExchangeAction[]; }) import { useTranslation } from 'react-i18next'; import { useCardDimensions } from '../../hooks/useCardDimensions'; import { focusRingCard, selectedCardStyle } from '../../styles/cardStyles'; import type { DaifugoPlayerData } from '../../types/card'; import { playerName } from '../../utils/playerUtils'; import { CardImage } from '../CardImage'; import { StatusBadge } from '../StatusBadge'; import { playerAreaClass } from './DaifugoCpuArea'; interface HumanPlayerAreaProps { player: DaifugoPlayerData; selectedIndices: number[]; onToggle: (idx: number) => void; isCurrentTurn: boolean; onDragCard: (idx: number) => void; } /** Renders the human player's hand area for Daifugo with card selection and drag support. */ export function DaifugoHumanArea({ player, selectedIndices, onToggle, isCurrentTurn, onDragCard, }: HumanPlayerAreaProps) ⋮---- aria-pressed= ⋮---- onClick= import { useTranslation } from 'react-i18next'; import type { DaifugoResponse } from '../../types/card'; ⋮---- /** Renders active rule badges (revolution, suit lock, eleven back, etc.) for Daifugo. */ import { useTranslation } from 'react-i18next'; import type { DaifugoConfigInput } from '../../types/card'; import type { SettingsGroup } from '../common/SettingsPanel'; import { SettingsPanel } from '../common/SettingsPanel'; interface DaifugoSettingsPanelProps { config: DaifugoConfigInput; onChange: (key: keyof DaifugoConfigInput, value: boolean | number) => void; } /** Renders the Daifugo game rule settings panel. */ export function DaifugoSettingsPanel( ⋮---- const checkbox = (key: keyof DaifugoConfigInput, label: string) => ( ⋮---- return import { useReducedMotion } from '../../hooks/useReducedMotion'; /** Props for the CountdownBar component. */ interface CountdownBarProps { /** Seconds remaining in the countdown. */ remaining: number; /** Total countdown duration in seconds. */ total: number; /** Visible label text (e.g. "残り 10 秒"). */ label?: string; } ⋮---- /** Seconds remaining in the countdown. */ ⋮---- /** Total countdown duration in seconds. */ ⋮---- /** Visible label text (e.g. "残り 10 秒"). */ ⋮---- /** Returns the design-system background class for the countdown bar based on remaining seconds. */ function barColorClass(remaining: number): string /** * Visual countdown progress bar with color-coded urgency and screen-reader support. * Renders a horizontal bar that shrinks as time runs out, transitioning green → yellow → red. */ import { useTranslation } from 'react-i18next'; import { playerAreaBase } from '../../styles/gameStyles'; import type { DoubtPlayerData } from '../../types/card'; import { CpuTurnArea } from '../CpuTurnArea'; /** CSS class for Doubt player area layout. */ ⋮---- /** Renders a CPU player area for Doubt with card count and tell indicator. */ ⋮---- import { useCardDimensions } from '../../hooks/useCardDimensions'; import { focusRingCard, selectedCardStyle } from '../../styles/cardStyles'; import type { Card } from '../../types/card'; import { CardImage } from '../CardImage'; interface HandCardProps { card: Card; index: number; selected: boolean; selectable: boolean; onToggle: (idx: number) => void; } /** Renders a selectable hand card for Doubt with selection highlight. */ export function DoubtHandCard( import { useTranslation } from 'react-i18next'; import type { GoFishBook } from '../../types/card'; import { valueName } from '../../utils/cardUtils'; interface GoFishBooksDisplayProps { books: GoFishBook[]; } /** Renders a list of completed 4-of-a-kind books. */ import { useTranslation } from 'react-i18next'; import type { GoFishPlayerData } from '../../types/card'; import { playerName } from '../../utils/playerUtils'; interface GoFishPlayerAreaProps { player: GoFishPlayerData; isSelected: boolean; onSelect: (idx: number) => void; disabled: boolean; } /** Renders an opponent's card count and book count, clickable to select as ask target. */ export function GoFishPlayerArea( import { useTranslation } from 'react-i18next'; import { SettingsPanel } from '../common/SettingsPanel'; interface GoFishSettingsDialogProps { cpuDifficulty: number; onCpuDifficultyChange: (value: string) => void; } /** CPU difficulty options for Go Fish. */ ⋮---- /** Renders Go Fish settings panel with CPU difficulty selector. */ ⋮---- title= import type { ReactNode } from 'react'; /** Props for the HintPulse wrapper component. */ export interface HintPulseProps { /** Whether the pulse animation is active. */ active: boolean; /** Whether the user prefers reduced motion. */ reducedMotion: boolean; /** Child elements to wrap. */ children: ReactNode; } ⋮---- /** Whether the pulse animation is active. */ ⋮---- /** Whether the user prefers reduced motion. */ ⋮---- /** Child elements to wrap. */ ⋮---- /** Wraps children with a pulse animation or static icon when a hint is active. */ import type { HintConfidence } from '../../types/hint'; /** Props for the HintTooltip component. */ export interface HintTooltipProps { /** The hint reasoning text (already translated). */ reason: string; /** Confidence level of the hint. */ confidence: HintConfidence; } ⋮---- /** The hint reasoning text (already translated). */ ⋮---- /** Confidence level of the hint. */ ⋮---- /** Displays a tooltip with the hint reasoning and confidence indicator. */ export function HintTooltip( import { motion } from 'framer-motion'; import { useRef } from 'react'; import { useReducedMotion } from '../../hooks/useReducedMotion'; import { dealSpring, hoverLift, selectLift } from '../../styles/motionPresets'; import { CardImage } from '../CardImage'; interface AnimatedCardProps extends React.ComponentProps { /** Stagger delay in seconds for deal animation. */ dealDelay?: number; /** Whether this card is selected (lift + glow). */ isSelected?: boolean; /** Shared layout animation ID. */ layoutId?: string; /** Additional class name for the motion wrapper div. */ wrapperClassName?: string; /** Callback fired when the deal-in animation completes. */ onDealComplete?: () => void; } ⋮---- /** Stagger delay in seconds for deal animation. */ ⋮---- /** Whether this card is selected (lift + glow). */ ⋮---- /** Shared layout animation ID. */ ⋮---- /** Additional class name for the motion wrapper div. */ ⋮---- /** Callback fired when the deal-in animation completes. */ ⋮---- /** Renders an animated face-up playing card with deal and select animations. */ export function AnimatedCard({ dealDelay = 0, isSelected = false, layoutId, wrapperClassName, onDealComplete, ...rest }: AnimatedCardProps) ⋮---- // Guard: onAnimationComplete fires for any animation (hover, selection), not just the initial deal. // Use a ref so the deal callback fires exactly once per component instance. import { motion } from 'framer-motion'; import { useRef } from 'react'; import { useReducedMotion } from '../../hooks/useReducedMotion'; import { flipSpring } from '../../styles/motionPresets'; import { CardBack } from '../CardImage'; interface AnimatedCardBackProps extends React.ComponentProps { /** Stagger delay in seconds for deal animation. */ dealDelay?: number; /** Shared layout animation ID. */ layoutId?: string; /** Callback fired when the flip-in animation completes. */ onFlipComplete?: () => void; } ⋮---- /** Stagger delay in seconds for deal animation. */ ⋮---- /** Shared layout animation ID. */ ⋮---- /** Callback fired when the flip-in animation completes. */ ⋮---- /** Renders an animated face-down card back with deal and optional flip animation. */ export function AnimatedCardBack( ⋮---- // Guard: onAnimationComplete fires for any animation, not just the initial flip. // Use a ref so the flip callback fires exactly once per component instance. import { useReducedMotion } from '../../hooks/useReducedMotion'; import type { Card } from '../../types/card'; import { CardImage } from '../CardImage'; import { AnimatedCard } from './AnimatedCard'; /** Cascade batch size: max concurrent animations per group. */ ⋮---- /** Delay between cascade batches in seconds. */ ⋮---- /** Stagger between cards within a batch in seconds. */ ⋮---- interface AnimatedPileProps { /** Cards in this pile (bottom to top). */ cards: Card[]; /** Layout mode: stacked (overlapping) or fanned (spread). */ layout: 'stacked' | 'fanned'; /** Card width in pixels. */ cardWidth?: number; /** Callback fired when each card lands in the pile. */ onPlace?: () => void; /** Enable cascade mode for auto-complete sequences. */ cascade?: boolean; /** Callback fired after all cards in cascade have landed. */ onComplete?: () => void; } ⋮---- /** Cards in this pile (bottom to top). */ ⋮---- /** Layout mode: stacked (overlapping) or fanned (spread). */ ⋮---- /** Card width in pixels. */ ⋮---- /** Callback fired when each card lands in the pile. */ ⋮---- /** Enable cascade mode for auto-complete sequences. */ ⋮---- /** Callback fired after all cards in cascade have landed. */ ⋮---- /** Computes the deal delay for a card in cascade mode, batching for performance. */ function getCascadeDelay(index: number): number /** * Renders a pile of animated cards with stacked or fanned layout. * In cascade mode, batches animations in groups of 5 with inter-group delays * to stay within the mobile performance budget. */ ⋮---- onPlace?.(); import { motion } from 'framer-motion'; import { useTranslation } from 'react-i18next'; import { useReducedMotion } from '../../hooks/useReducedMotion'; interface LossFeedbackProps { /** Whether to show the loss feedback. */ show: boolean; } ⋮---- /** Whether to show the loss feedback. */ ⋮---- /** * Renders a loss feedback overlay as an edge vignette using the error color. * Includes ARIA live region for screen readers. Reduced motion users see a * text banner instead of the animated vignette. */ ⋮---- // Reduced motion: show text banner instead of animated vignette import { motion } from 'framer-motion'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useReducedMotion } from '../../hooks/useReducedMotion'; interface WinCelebrationProps { /** Whether to show the celebration animation. */ show: boolean; /** Delay in ms before particles appear. Default: 400 (the "tension beat"). */ delayMs?: number; /** Callback fired when particles start animating (use for sound sync). */ onCelebrate?: () => void; } ⋮---- /** Whether to show the celebration animation. */ ⋮---- /** Delay in ms before particles appear. Default: 400 (the "tension beat"). */ ⋮---- /** Callback fired when particles start animating (use for sound sync). */ ⋮---- // Design system tokens: accent gold (60%), success green (20%), warm ivory (20%) ⋮---- interface Particle { id: number; x: number; y: number; color: string; size: number; delay: number; } function generateParticles(): Particle[] /** Renders a celebration animation on win with configurable delay and sound callback. */ ⋮---- // Reduced motion: show text banner instead of particles import { useTranslation } from 'react-i18next'; import { useCardDimensions } from '../../hooks/useCardDimensions'; import type { Card } from '../../types/card'; import { CardImage } from '../CardImage'; /** Renders the discarded pairs area for Old Maid. */ ⋮---- // Group cards into pairs (every 2 cards is one discarded pair) ⋮---- // If odd number of cards, show the last one alone import { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import type { DrawHistoryEntry, OldMaidPlayerData } from '../../types/card'; import { findPlayerName } from '../../utils/playerUtils'; /** Renders a scrollable timeline of draw history entries for Old Maid. */ ⋮---- // biome-ignore lint/correctness/useExhaustiveDependencies: entries triggers scroll on new history entries import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useCardDimensions } from '../../hooks/useCardDimensions'; import { playerAreaBase } from '../../styles/gameStyles'; import type { OldMaidPlayerData } from '../../types/card'; import { playerName } from '../../utils/playerUtils'; import { CardBack, CardImage } from '../CardImage'; import { StatusBadge } from '../StatusBadge'; /** CSS class for Old Maid player area layout. */ ⋮---- interface PlayerAreaProps { player: OldMaidPlayerData; isTarget: boolean; isHumanTurn: boolean; gameEndFlag: boolean; loading: boolean; highlightedCardIdx: number; isSuspect?: boolean; onToggleSuspect?: () => void; onDraw: (drawIdx: number) => void; onReorder?: (indices: number[]) => void; /** When true, non-target CPU players hide card back images to save space. */ compactNonTarget?: boolean; } ⋮---- /** When true, non-target CPU players hide card back images to save space. */ ⋮---- /** Renders a player area for Old Maid with draw targets, hand display, and reorder support. */ export function OldMaidPlayerArea({ player, isTarget, isHumanTurn, gameEndFlag, loading, highlightedCardIdx, isSuspect, onToggleSuspect, onDraw, onReorder, compactNonTarget, }: PlayerAreaProps) ⋮---- const handleKeyDown = (e: React.KeyboardEvent) => ⋮---- const swapAndReorder = (index1: number, index2: number) => ⋮---- // biome-ignore lint/style/noNonNullAssertion: guard on line 52 ensures player.cards is non-null ⋮---- onDrop= import { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { OldMaidMode } from '../../hooks/useOldMaidGame'; import { btnPrimary, btnSecondary } from '../../styles/buttonStyles'; import { getFocusableElements } from '../../utils/dom'; /** Props for the OldMaidSettingsDialog component. */ interface OldMaidSettingsDialogProps { open: boolean; mode: number; cpuPlacementStrategy: boolean; cpuMemoryAI: boolean; cpuHesitationEnabled: boolean; cpuMetaAI: boolean; onModeChange: (m: number) => void; onStrategyChange: (v: boolean) => void; onMemoryAIChange: (v: boolean) => void; onHesitationChange: (v: boolean) => void; onMetaAIChange: (v: boolean) => void; onApply: () => void; onClose: () => void; } /** Renders a modal dialog for Old Maid game settings (mode and CPU options). */ ⋮---- const handleKeyDown = (e: KeyboardEvent) => ⋮---- // biome-ignore lint/a11y/noStaticElementInteractions: overlay backdrop dismisses dialog on click import { useTranslation } from 'react-i18next'; import { useIsMobile } from '../../hooks/useCardDimensions'; import { suitName, valueName } from '../../utils/cardUtils'; import { isPositionPlaced, isPositionPlayable, SUITS } from '../../utils/sevensUtils'; import { ScrollFadeHint } from '../ScrollFadeHint'; interface BoardProps { tablePlaced: number[]; tunnelEnabled: boolean; tunnelSkipWidth: number; endStopEnabled: boolean; jokerSelecting: boolean; onJokerPlace?: (suit: number, value: number) => void; } /** Cell background and text color style based on card placement state. */ function cellColors(placed: boolean, isCenter: boolean, canPlace: boolean): React.CSSProperties ⋮---- import { useTranslation } from 'react-i18next'; import type { SevensPlayerData } from '../../types/card'; import { playerAreaClass } from '../../utils/sevensUtils'; import { CpuTurnArea } from '../CpuTurnArea'; function SevCpuArea( ⋮---- ⋮---- /** Renders a CPU player area for Sevens with card count and pass info. */ import { useTranslation } from 'react-i18next'; import { useCardDimensions } from '../../hooks/useCardDimensions'; import { focusRingWhite } from '../../styles/buttonStyles'; import { playableCardStyle } from '../../styles/cardStyles'; import type { Card, SevensPlayerData } from '../../types/card'; import { valueName } from '../../utils/cardUtils'; import { playerName } from '../../utils/playerUtils'; import { isCardPlayable, playerAreaClass } from '../../utils/sevensUtils'; import { CardImage } from '../CardImage'; import { StatusBadge } from '../StatusBadge'; interface HumanAreaProps { player: SevensPlayerData; isCurrentTurn: boolean; tablePlaced: number[]; tunnelEnabled: boolean; tunnelSkipWidth: number; noJokerFinish: boolean; endStopEnabled: boolean; jokerConsecutiveBanned: boolean; loading: boolean; onPlay: (idx: number) => void; } ⋮---- ⋮---- onClick= ⋮---- /** Renders the human player area for Sevens with playable card highlighting. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Baccarat page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the BlackJack page. */ export function BlackJackSkeleton() import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Bridge page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { gameTheme } from '../../styles/gameTheme'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Crazy Eights page. */ export function CrazyEightsSkeleton() import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Cribbage page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Daifugo page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { gameTheme } from '../../styles/gameTheme'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Doubt page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Euchre page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonCard } from './SkeletonCard'; /** Renders a loading skeleton placeholder for the FreeCell page. */ ⋮---- {/* FreeCells + Foundation row */} ⋮---- {/* Tableau columns */} import type { ReactNode } from 'react'; import { GameFooter } from '../GameFooter'; import { SkeletonBar } from './SkeletonBar'; /** Props for the shared game skeleton shell. */ interface GameSkeletonProps { /** Background color class for the outer wrapper (e.g. "bg-game-bg-green-bright"). */ bgClass: string; /** Classes for the scrollable body area. Defaults to "pt-3 px-4". */ bodyClassName?: string; /** Classes for the GameFooter (background, border, padding). */ footerClassName: string; /** Body content rendered inside the scrollable area. */ children: ReactNode; /** Footer content rendered inside GameFooter. */ footer: ReactNode; } ⋮---- /** Background color class for the outer wrapper (e.g. "bg-game-bg-green-bright"). */ ⋮---- /** Classes for the scrollable body area. Defaults to "pt-3 px-4". */ ⋮---- /** Classes for the GameFooter (background, border, padding). */ ⋮---- /** Body content rendered inside the scrollable area. */ ⋮---- /** Footer content rendered inside GameFooter. */ ⋮---- /** Renders the shared outer shell for game skeleton placeholders. */ export function GameSkeleton({ bgClass, bodyClassName = 'pt-3 px-4', footerClassName, children, footer, }: GameSkeletonProps) import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Gin Rummy page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Go Fish page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonCard } from './SkeletonCard'; /** Renders a loading skeleton placeholder for the Golf Solitaire page. */ ⋮---- {/* Golf: 7 columns × 5 rows placeholder */} ⋮---- {/* Stock/Waste placeholder */} import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Hearts page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Texas Hold'em page. */ export function HoldemSkeleton() import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonCard } from './SkeletonCard'; /** Renders a loading skeleton placeholder for the Klondike page. */ ⋮---- {/* Foundation + Stock/Waste row */} ⋮---- {/* Tableau columns */} import { gameTheme } from '../../styles/gameTheme'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonGrid } from './SkeletonGrid'; /** Renders a loading skeleton placeholder for the Memory page. */ export function MemorySkeleton() import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Napoleon page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Oh Hell page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Old Maid page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Omaha Hold'em page. */ export function OmahaSkeleton() import { GameSkeleton } from './GameSkeleton'; /** Renders a loading skeleton placeholder for the Pig's Tail page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Poker page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonCard } from './SkeletonCard'; /** Renders a loading skeleton placeholder for the Pyramid page. */ ⋮---- {/* Pyramid rows placeholder */} ⋮---- {/* Stock/Waste placeholder */} import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonGrid } from './SkeletonGrid'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Sevens page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Short Deck Hold'em page. */ export function ShortDeckSkeleton() interface SkeletonBarProps { height?: string; className?: string; } /** Renders an animated skeleton loading bar placeholder. */ export function SkeletonBar( interface SkeletonCardProps { width: number; height: number; className?: string; } /** Renders an animated skeleton card placeholder. */ export function SkeletonCard( interface SkeletonGridProps { count: number; cols: string; aspectRatio?: string; /** Extra classes applied to the grid container. */ gridClassName?: string; } ⋮---- /** Extra classes applied to the grid container. */ ⋮---- /** Renders an animated skeleton grid of card placeholders. */ export function SkeletonGrid( import { SkeletonCard } from './SkeletonCard'; interface SkeletonHandProps { cardWidth: number; cardHeight: number; count?: number; className?: string; } /** Renders an animated skeleton hand of card placeholders. */ export function SkeletonHand( import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Spades page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Speed page. */ ⋮---- {/* CPU hand placeholder */} ⋮---- {/* Center piles placeholder */} ⋮---- {/* Human hand placeholder */} import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonCard } from './SkeletonCard'; /** Renders a loading skeleton placeholder for the Spider Solitaire page. */ ⋮---- {/* Stock + Completed row */} ⋮---- {/* Tableau columns (10 columns) */} import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonHand } from './SkeletonHand'; /** Renders a loading skeleton placeholder for the Three Card Poker page. */ import { useCardDimensions } from '../../hooks/useCardDimensions'; import { GameSkeleton } from './GameSkeleton'; import { SkeletonCard } from './SkeletonCard'; /** Renders a loading skeleton placeholder for the TriPeaks page. */ ⋮---- {/* TriPeaks rows placeholder: 3, 6, 9, 10 cards */} ⋮---- {/* Stock/Waste placeholder */} import { useTranslation } from 'react-i18next'; import { useTutorialContext } from '../../providers/TutorialProvider'; import { btnSecondary } from '../../styles/buttonStyles'; /** Tutorial button that starts the game tutorial. */ export function TutorialButton() ⋮---- aria-label= import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from 'react'; import type { TutorialStep } from '../../types/tutorial'; import { getFocusableElements } from '../ConfirmDialog'; import { TutorialTooltip } from './TutorialTooltip'; /** Rect representing position and size of an element. */ interface SpotlightRect { top: number; left: number; width: number; height: number; } /** Padding around the spotlight cutout. */ ⋮---- /** Border radius for the spotlight cutout. */ ⋮---- /** Minimum distance between tooltip and viewport edge in pixels. */ ⋮---- /** Props for the TutorialOverlay component. */ export interface TutorialOverlayProps { /** The current tutorial step definition. */ step: TutorialStep; /** Zero-based index of the current step. */ stepIndex: number; /** Total number of steps. */ totalSteps: number; /** Called to advance to the next step. */ onNext: () => void; /** Called to skip/dismiss the tutorial. */ onSkip: () => void; /** Whether reduced motion is preferred. */ reducedMotion: boolean; } ⋮---- /** The current tutorial step definition. */ ⋮---- /** Zero-based index of the current step. */ ⋮---- /** Total number of steps. */ ⋮---- /** Called to advance to the next step. */ ⋮---- /** Called to skip/dismiss the tutorial. */ ⋮---- /** Whether reduced motion is preferred. */ ⋮---- /** Computes spotlight rect from a target element. */ function getSpotlightRect(el: Element | null): SpotlightRect | null /** Computes tooltip position based on spotlight rect and placement. */ function getTooltipStyle(rect: SpotlightRect | null, placement: TutorialStep['placement']): React.CSSProperties /** Renders a full-screen overlay with a spotlight cutout and tooltip for the tutorial. */ ⋮---- // Lock background scroll while overlay is visible ⋮---- // Find and observe the target element ⋮---- // Listen for click on target when advanceOn is 'click'. // Note: the target must be within the spotlight cutout for clicks to reach it, // as the overlay dialog intercepts clicks outside the cutout area. ⋮---- const handler = () ⋮---- // Focus trap ⋮---- const handleTab = (e: KeyboardEvent) => ⋮---- // Clamp tooltip within viewport after render — depends on tooltipStyle which // is derived from spotlightRect and step.placement, so it re-runs when they change. // biome-ignore lint/correctness/useExhaustiveDependencies: tooltipStyle captures spotlightRect+placement changes ⋮---- {/* SVG overlay with spotlight cutout */} ⋮---- {/* Tooltip positioned relative to spotlight */} import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { useTutorialProgress } from '../../hooks/useTutorialProgress'; /** Collapsible panel showing tutorial completion progress across all games. */ ⋮---- {/* Progress bar */} ⋮---- {/* Game grid */} ⋮---- title= import { useEffect, useLayoutEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { btnPrimary, btnSecondary } from '../../styles/buttonStyles'; import { getFocusableElements } from '../../utils/dom'; /** Props for the TutorialSuggestDialog component. */ export interface TutorialSuggestDialogProps { /** Whether the dialog is visible. */ open: boolean; /** Called when the user chooses to start the tutorial. */ onStartTutorial: () => void; /** Called when the user skips the tutorial suggestion. */ onSkip: () => void; /** Whether the "don't show again" checkbox is checked. */ dontShowAgain: boolean; /** Called when the "don't show again" checkbox changes. */ onDontShowAgainChange: (checked: boolean) => void; } ⋮---- /** Whether the dialog is visible. */ ⋮---- /** Called when the user chooses to start the tutorial. */ ⋮---- /** Called when the user skips the tutorial suggestion. */ ⋮---- /** Whether the "don't show again" checkbox is checked. */ ⋮---- /** Called when the "don't show again" checkbox changes. */ ⋮---- /** Dialog shown on first visit to suggest starting the tutorial. */ ⋮---- // Lock background scroll while dialog is visible ⋮---- const handleKeyDown = (e: KeyboardEvent) => ⋮---- // biome-ignore lint/a11y/noStaticElementInteractions: overlay backdrop dismisses dialog on click ⋮---- onKeyDown= import { useTranslation } from 'react-i18next'; import { btnPrimary, btnSecondary } from '../../styles/buttonStyles'; import type { TutorialAdvanceOn } from '../../types/tutorial'; /** Props for the TutorialTooltip component. */ export interface TutorialTooltipProps { /** The message to display in the tooltip. */ message: string; /** Zero-based index of the current step. */ stepIndex: number; /** Total number of steps. */ totalSteps: number; /** Called when advancing to the next step. */ onNext: () => void; /** Called when the tutorial is skipped. */ onSkip: () => void; /** How to advance: 'next' shows a next button, 'click' hides it. */ advanceOn: TutorialAdvanceOn; } ⋮---- /** The message to display in the tooltip. */ ⋮---- /** Zero-based index of the current step. */ ⋮---- /** Total number of steps. */ ⋮---- /** Called when advancing to the next step. */ ⋮---- /** Called when the tutorial is skipped. */ ⋮---- /** How to advance: 'next' shows a next button, 'click' hides it. */ ⋮---- /** Renders a glass-panel tooltip with step indicator and navigation buttons for the tutorial. */ import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { TutorialProvider } from '../../providers/TutorialProvider'; import type { TutorialStep } from '../../types/tutorial'; /** Props for the TutorialWrapper component. */ export interface TutorialWrapperProps { /** Game identifier used for i18n namespace and tutorial localStorage key. */ gameName: string; /** Ordered list of tutorial steps for this game. */ steps: TutorialStep[]; /** Child elements rendered inside the tutorial context. */ children: ReactNode; } ⋮---- /** Game identifier used for i18n namespace and tutorial localStorage key. */ ⋮---- /** Ordered list of tutorial steps for this game. */ ⋮---- /** Child elements rendered inside the tutorial context. */ ⋮---- /** Wraps a game page with TutorialProvider, providing i18n translation automatically. */ import { useEffect, useId, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { btnPrimary, btnSecondary } from '../styles/buttonStyles'; import type { ActionLogEntry } from '../types/card'; import { cardLabel } from '../utils/cardUtils'; interface ActionLogPanelProps { entries: ActionLogEntry[]; onClose: () => void; } function formatEntry(entry: ActionLogEntry, t: (key: string, opts?: Record) => string): string function getFocusableElements(container: HTMLElement): HTMLElement[] /** Renders a panel displaying game action log entries with copy and download. */ ⋮---- const handleKeyDown = (e: KeyboardEvent) => ⋮---- const handleCopy = async () => const handleClose = () => const handleDownload = () => import { useTranslation } from 'react-i18next'; import { btnSecondary } from '../styles/buttonStyles'; import type { ActionLogEntry } from '../types/card'; import { ActionLogPanel } from './ActionLogPanel'; interface ActionLogSectionProps { isEndPhase: boolean; actionLog: ActionLogEntry[] | null; showActionLog: () => void; hideActionLog: () => void; } /** Renders the action log view button and panel, shown at end phase. */ import { useTranslation } from 'react-i18next'; import { btnPokerAccent, btnPokerAllIn, btnPokerMuted, btnPokerPrimary } from '../styles/buttonStyles'; interface BettingControlsProps { inputId: string; betAmount: number; onBetAmountChange: (v: number) => void; minRaise: number; maxBetAmount?: number; hasOutstandingBet: boolean; loading: boolean; onCall: () => void; onRaise: () => void; onBet: () => void; onCheck: () => void; onFold: () => void; onAllIn: () => void; } /** Renders betting action buttons (call/raise/bet/check/fold/all-in) with amount input. */ import { useTranslation } from 'react-i18next'; import { focusRingWhite } from '../styles/buttonStyles'; import type { Card } from '../types/card'; import { cardAlt } from '../utils/cardAlt'; function getImagePath(card: Card): string ⋮---- const zeroPad = (n: number) ⋮---- interface CardImageProps { card: Card; width?: number; style?: React.CSSProperties; className?: string; draggable?: boolean; onDragStart?: (e: React.DragEvent) => void; onDragOver?: (e: React.DragEvent) => void; onDrop?: (e: React.DragEvent) => void; } /** Natural card image dimensions (200x300 PNG assets, 2:3 aspect ratio). */ ⋮---- /** Renders a face-up playing card image. */ export function CardImage({ card, width, style, className, draggable, onDragStart, onDragOver, onDrop, }: CardImageProps) ⋮---- src= ⋮---- interface CardBackProps { width?: number; style?: React.CSSProperties; className?: string; onClick?: () => void; /** Only applies when onClick is provided (button mode). */ ariaLabel?: string; } ⋮---- /** Only applies when onClick is provided (button mode). */ ⋮---- /** Renders a face-down card back image, optionally as a clickable button. */ export function CardBack( import { useEffect, useRef } from 'react'; import { btnDanger, btnSecondary } from '../styles/buttonStyles'; import { getFocusableElements } from '../utils/dom'; // Re-export for backward compatibility with existing imports ⋮---- /** Props for the ConfirmDialog component. */ export interface ConfirmDialogProps { open: boolean; title: string; message: string; confirmLabel: string; cancelLabel: string; onConfirm: () => void; onCancel: () => void; } /** Renders a modal confirmation dialog with confirm and cancel buttons. */ export function ConfirmDialog(props: ConfirmDialogProps) ⋮---- // Safe assertion: useEffect runs after render, so ref is always attached when open=true ⋮---- const handleKeyDown = (e: KeyboardEvent) => ⋮---- // biome-ignore lint/a11y/noStaticElementInteractions: overlay backdrop dismisses dialog on click ⋮---- {/* biome-ignore lint/a11y/useKeyWithClickEvents: keyboard events handled at document level via useEffect */} import { useTranslation } from 'react-i18next'; import { useIsMobile } from '../hooks/useCardDimensions'; interface CpuAccordionProps { children: React.ReactNode; playerCount: number; dataTutorial?: string; } /** Collapsible wrapper for CPU player cards. Closed by default on mobile to reduce scroll. */ export function CpuAccordion( import { useTranslation } from 'react-i18next'; import { bettingActionName } from '../styles/gameConstants'; interface CpuActionLogProps { actions: { playerIdx: number; action: number; amount: number }[] | undefined; } /** Renders a log of CPU betting actions. */ ⋮---- aria-label= ⋮---- import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { bettingActionName } from '../styles/gameConstants'; interface CpuActionToastProps { actions: { playerIdx: number; action: number; amount: number }[] | undefined; } ⋮---- /** Toast notification for CPU betting actions. Auto-dismisses after 3 seconds. */ export function CpuActionToast( ⋮---- import { useTranslation } from 'react-i18next'; import { useCardDimensions } from '../hooks/useCardDimensions'; import { handNameBadgeClass } from '../styles/gameConstants'; import type { Card } from '../types/card'; import { deriveAdaptationLevel, deriveStrategyStyle } from '../utils/metaAiAdaptation'; import { CardBack, CardImage } from './CardImage'; import { MetaAiIndicator } from './MetaAiIndicator'; import { MetaAiToast } from './MetaAiToast'; /** Meta-AI data for a CPU player. */ export interface CpuMetaAiProp { /** Whether meta-AI is enabled for this CPU. */ enabled: boolean; /** Number of games the meta-AI has observed. */ gamesPlayed: number; /** Bluff rate (used by betting/doubt games). */ bluffRate?: number; /** Fold rate (used by betting games). */ foldRate?: number; /** Edge pick rate (used by OldMaid). */ edgePickRate?: number; } ⋮---- /** Whether meta-AI is enabled for this CPU. */ ⋮---- /** Number of games the meta-AI has observed. */ ⋮---- /** Bluff rate (used by betting/doubt games). */ ⋮---- /** Fold rate (used by betting games). */ ⋮---- /** Edge pick rate (used by OldMaid). */ ⋮---- interface CpuPlayerCardProps { player: { id: number; playStyleName: string; chips: number; currentBet: number; folded: boolean; allIn: boolean; handName: string; cards: Card[]; }; showCards: boolean; faceDownCount: number; showHandName: boolean; extraInfo?: React.ReactNode; /** When true, show text card count instead of face-down card images. */ compactFaceDown?: boolean; /** Optional meta-AI data for visual feedback. */ metaAi?: CpuMetaAiProp; } ⋮---- /** When true, show text card count instead of face-down card images. */ ⋮---- /** Optional meta-AI data for visual feedback. */ ⋮---- /** Renders a CPU player's info area with cards (face-up or face-down) and status. */ ⋮---- ⋮---- import { useTranslation } from 'react-i18next'; import { activeTurnClass, finishedPlayerClass } from '../styles/gameConstants'; import { playerName } from '../utils/playerUtils'; import { StatusBadge } from './StatusBadge'; interface CpuTurnAreaProps { id?: string; playerId: number; isHuman: boolean; isCurrentTurn: boolean; isFinished: boolean; dimFinished?: boolean; finishedLabel?: string; className: string; nameClassName?: string; children?: React.ReactNode; } /** Renders a player area with name, turn indicator, and optional children. */ ⋮---- {playerName(playerId, isHuman)} {isFinished && finishedLabel && {finishedLabel}} {isCurrentTurn && !isFinished && {t('status.thinking')}} {children} ); import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useLocation } from 'react-router-dom'; import { gameCategories, gameRoutes } from '../constants/gameRoutes'; import { SITE_NAME } from '../constants/site'; import { useFavoriteGames } from '../hooks/useFavoriteGames'; import { focusRingWhite } from '../styles/buttonStyles'; import { SoundToggle } from './SoundToggle'; import { TutorialProgressPanel } from './tutorial/TutorialProgressPanel'; /** Lookup map from path to game route for favorite rendering. */ ⋮---- /** Persistent left sidebar navigation for large desktop (≥1024px) with search, favorites, categories, and tutorial progress. */ ⋮---- /** Pre-compute bilingual names for search filtering. */ ⋮---- /** Filter game routes by bilingual name match. */ ⋮---- {/* Site name */} ⋮---- {/* Search */} ⋮---- onClick= ⋮---- {/* Scrollable game list */} ⋮---- {/* Favorites */} ⋮---- {/* Search results or category list */} ⋮---- ⋮---- {/* Tutorial progress */} ⋮---- {/* Language + Sound controls */} import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import type { HoldemEquity } from '../types/card'; interface EquityDisplayProps { equity: HoldemEquity; potOdds: number; } /** Renders a Hold'em equity display with win probability, pot odds, and hand odds table. */ import { useTranslation } from 'react-i18next'; interface ErrorAlertProps { message: string | null; onRetry?: () => void; } /** Renders an error alert banner with optional retry button, hidden when message is null. */ export function ErrorAlert( import type { ErrorInfo, ReactNode } from 'react'; import { Component } from 'react'; import type { WithTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next'; import { btnPrimary } from '../styles/buttonStyles'; interface ErrorBoundaryProps extends WithTranslation { children: ReactNode; } interface ErrorBoundaryState { hasError: boolean; } ⋮---- constructor(props: ErrorBoundaryProps) static getDerivedStateFromError(): ErrorBoundaryState componentDidCatch(error: Error, info: ErrorInfo): void ⋮---- /** Error boundary component that catches render errors and shows a retry screen. */ interface GameFooterProps { className?: string; children: React.ReactNode; /** When true, applies glassmorphism + rounded top corners + scroll on mobile. */ floating?: boolean; } ⋮---- /** When true, applies glassmorphism + rounded top corners + scroll on mobile. */ ⋮---- /** Renders a sticky footer area for game action buttons with safe-area padding. */ export function GameFooter( import { useTranslation } from 'react-i18next'; interface GameMessageBoxProps { message: string | undefined; messageCode?: string; messageParams?: Record; alwaysVisible?: boolean; /** "info" (default): polite announcement, "alert": assertive announcement for game results. */ severity?: 'info' | 'alert'; } ⋮---- /** "info" (default): polite announcement, "alert": assertive announcement for game results. */ ⋮---- /** Renders a game message box with i18n translation support via messageCode. */ export function GameMessageBox({ message, messageCode, messageParams, alwaysVisible = false, severity = 'info', }: GameMessageBoxProps) /** Renders a visually-hidden h1 heading for the current game page (WCAG 2.4.6). */ export function GamePageHeading( import type { ReactNode } from 'react'; import { GamePageHeading } from './GamePageHeading'; import { GameResetDialog } from './GameResetDialog'; import { ManualButton } from './ManualButton'; import { WinCelebration } from './motion/WinCelebration'; import { PhaseIndicator } from './PhaseIndicator'; import { TutorialButton } from './tutorial/TutorialButton'; /** Props for the GamePageShell component. */ export interface GamePageShellProps { /** Page title rendered as a visually-hidden h1 heading. */ title: string; /** Tailwind background class for the outer container (e.g., gameTheme.hearts.bg). */ gameThemeBg: string; /** Current phase label shown in the PhaseIndicator. */ phaseName: string; /** Whether it is the human player's turn, controls the turn indicator color. */ isHumanTurn: boolean; /** Path used by ManualButton to load the game manual (e.g., "/hearts"). */ gamePath: string; /** Whether a game-end condition has been reached, controls WinCelebration. */ gameEndFlag: boolean; /** Whether an async operation is in progress; forwarded to aria-busy on the outer container. */ loading: boolean; /** Whether the reset confirmation dialog is open. */ confirmOpen: boolean; /** Callback to confirm the reset action. */ confirmReset: () => void; /** Callback to cancel the reset action. */ cancelReset: () => void; /** Extra elements rendered inside the PhaseIndicator alongside TutorialButton/ManualButton. */ headerExtra?: ReactNode; /** * Game-specific content placed between the PhaseIndicator and the win/reset overlays. * Typically includes: settings panel, scrollable game area, and GameFooter. */ children: ReactNode; } ⋮---- /** Page title rendered as a visually-hidden h1 heading. */ ⋮---- /** Tailwind background class for the outer container (e.g., gameTheme.hearts.bg). */ ⋮---- /** Current phase label shown in the PhaseIndicator. */ ⋮---- /** Whether it is the human player's turn, controls the turn indicator color. */ ⋮---- /** Path used by ManualButton to load the game manual (e.g., "/hearts"). */ ⋮---- /** Whether a game-end condition has been reached, controls WinCelebration. */ ⋮---- /** Whether an async operation is in progress; forwarded to aria-busy on the outer container. */ ⋮---- /** Whether the reset confirmation dialog is open. */ ⋮---- /** Callback to confirm the reset action. */ ⋮---- /** Callback to cancel the reset action. */ ⋮---- /** Extra elements rendered inside the PhaseIndicator alongside TutorialButton/ManualButton. */ ⋮---- /** * Game-specific content placed between the PhaseIndicator and the win/reset overlays. * Typically includes: settings panel, scrollable game area, and GameFooter. */ ⋮---- /** * Renders the shared outer shell of a game page. * Includes the background container, visually-hidden heading, PhaseIndicator with * TutorialButton and ManualButton, and end-game overlays (WinCelebration, GameResetDialog). * Game-specific content is rendered via children. */ export function GamePageShell({ title, gameThemeBg, phaseName, isHumanTurn, gamePath, gameEndFlag, loading, confirmOpen, confirmReset, cancelReset, headerExtra, children, }: GamePageShellProps) import { useTranslation } from 'react-i18next'; import { ConfirmDialog } from './ConfirmDialog'; /** Props for the GameResetDialog component. */ export interface GameResetDialogProps { confirmOpen: boolean; confirmReset: () => void; cancelReset: () => void; } /** Renders a reset confirmation dialog using common translation keys. */ export function GameResetDialog( ⋮---- message= ⋮---- cancelLabel= /** Props for the LandscapeBanner component. */ interface LandscapeBannerProps { /** The message to display in the banner. */ message: string; } ⋮---- /** The message to display in the banner. */ ⋮---- /** Banner suggesting landscape orientation on small portrait screens. */ export function LandscapeBanner( import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { btnSecondary } from '../styles/buttonStyles'; import { ManualModal } from './ManualModal'; /** Props for the ManualButton component. */ export interface ManualButtonProps { gamePath: string; } /** Renders a button that opens the game manual modal for the given game path. */ export function ManualButton( ⋮---- aria-label= import { useEffect, useRef } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import type { Components } from 'react-markdown'; import Markdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { manualTexts } from '../constants/manualTexts'; import { btnSecondary } from '../styles/buttonStyles'; import { getFocusableElements } from '../utils/dom'; import { MermaidBlock } from './MermaidBlock'; /** Props for the ManualModal component. */ export interface ManualModalProps { open: boolean; onClose: () => void; gamePath: string; } /** Static remark plugins array — defined outside component to avoid re-creating on every render. */ ⋮---- /** Static markdown component overrides — defined outside component to avoid re-rendering the entire tree on every render. */ ⋮---- pre( ⋮---- // Unwrap
 when the child is a mermaid diagram rendered by the code override
⋮----
code(
⋮----
return 
⋮----
// biome-ignore lint/a11y/noStaticElementInteractions: overlay backdrop dismisses modal on click
⋮----
{/* biome-ignore lint/a11y/useKeyWithClickEvents: keyboard events handled at document level via useEffect */}
⋮----