From 55a80bc4bb88dc4fa69c0c3dc54abb80840d0508 Mon Sep 17 00:00:00 2001 From: Ryo Hirayama Date: Mon, 25 Dec 2023 17:30:58 +0900 Subject: [PATCH] Ultimatum view models --- crates/apps/xeejp/package-lock.json | 143 ++++++------ crates/apps/xeejp/package.json | 4 +- crates/apps/xeejp/pages/conduct.tsx | 36 +++ .../eagle-ultimatum/src/conductor_model.rs | 15 +- crates/games/eagle-ultimatum/src/game.rs | 206 ++++++++---------- crates/games/eagle-ultimatum/src/phase.rs | 35 +-- .../games/eagle-ultimatum/src/player_model.rs | 19 +- 7 files changed, 228 insertions(+), 230 deletions(-) diff --git a/crates/apps/xeejp/package-lock.json b/crates/apps/xeejp/package-lock.json index 69d8886..406c3ac 100644 --- a/crates/apps/xeejp/package-lock.json +++ b/crates/apps/xeejp/package-lock.json @@ -20,8 +20,8 @@ "fast-deep-equal": "^3.1.3", "flag-icons": "^6.7.0", "jotai": "^2.3.1", - "next": "13.4.6", - "postcss": "8.4.24", + "next": "^13.5.6", + "postcss": "^8.4.32", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.46.1", @@ -226,9 +226,9 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@next/env": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.6.tgz", - "integrity": "sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg==" + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz", + "integrity": "sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==" }, "node_modules/@next/eslint-plugin-next": { "version": "13.4.10", @@ -240,9 +240,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.6.tgz", - "integrity": "sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.6.tgz", + "integrity": "sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==", "cpu": [ "arm64" ], @@ -255,9 +255,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.6.tgz", - "integrity": "sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz", + "integrity": "sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==", "cpu": [ "x64" ], @@ -270,9 +270,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.6.tgz", - "integrity": "sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz", + "integrity": "sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==", "cpu": [ "arm64" ], @@ -285,9 +285,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.6.tgz", - "integrity": "sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz", + "integrity": "sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==", "cpu": [ "arm64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.6.tgz", - "integrity": "sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz", + "integrity": "sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==", "cpu": [ "x64" ], @@ -315,9 +315,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.6.tgz", - "integrity": "sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz", + "integrity": "sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==", "cpu": [ "x64" ], @@ -330,9 +330,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.6.tgz", - "integrity": "sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz", + "integrity": "sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==", "cpu": [ "arm64" ], @@ -345,9 +345,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.6.tgz", - "integrity": "sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz", + "integrity": "sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==", "cpu": [ "ia32" ], @@ -360,9 +360,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.6.tgz", - "integrity": "sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz", + "integrity": "sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==", "cpu": [ "x64" ], @@ -431,9 +431,9 @@ "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==" }, "node_modules/@swc/helpers": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", - "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "dependencies": { "tslib": "^2.4.0" } @@ -2925,9 +2925,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -2947,39 +2947,37 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/next": { - "version": "13.4.6", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.6.tgz", - "integrity": "sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/next/-/next-13.5.6.tgz", + "integrity": "sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==", "dependencies": { - "@next/env": "13.4.6", - "@swc/helpers": "0.5.1", + "@next/env": "13.5.6", + "@swc/helpers": "0.5.2", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", + "postcss": "8.4.31", "styled-jsx": "5.1.1", - "watchpack": "2.4.0", - "zod": "3.21.4" + "watchpack": "2.4.0" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=16.8.0" + "node": ">=16.14.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.6", - "@next/swc-darwin-x64": "13.4.6", - "@next/swc-linux-arm64-gnu": "13.4.6", - "@next/swc-linux-arm64-musl": "13.4.6", - "@next/swc-linux-x64-gnu": "13.4.6", - "@next/swc-linux-x64-musl": "13.4.6", - "@next/swc-win32-arm64-msvc": "13.4.6", - "@next/swc-win32-ia32-msvc": "13.4.6", - "@next/swc-win32-x64-msvc": "13.4.6" + "@next/swc-darwin-arm64": "13.5.6", + "@next/swc-darwin-x64": "13.5.6", + "@next/swc-linux-arm64-gnu": "13.5.6", + "@next/swc-linux-arm64-musl": "13.5.6", + "@next/swc-linux-x64-gnu": "13.5.6", + "@next/swc-linux-x64-musl": "13.5.6", + "@next/swc-win32-arm64-msvc": "13.5.6", + "@next/swc-win32-ia32-msvc": "13.5.6", + "@next/swc-win32-x64-msvc": "13.5.6" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "fibers": ">= 3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.3.0" @@ -2988,18 +2986,15 @@ "@opentelemetry/api": { "optional": true }, - "fibers": { - "optional": true - }, "sass": { "optional": true } } }, "node_modules/next/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -3008,10 +3003,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -3335,9 +3334,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", "funding": [ { "type": "opencollective", @@ -3353,7 +3352,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -4592,14 +4591,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", - "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/crates/apps/xeejp/package.json b/crates/apps/xeejp/package.json index 8e0a964..a040370 100644 --- a/crates/apps/xeejp/package.json +++ b/crates/apps/xeejp/package.json @@ -23,8 +23,8 @@ "fast-deep-equal": "^3.1.3", "flag-icons": "^6.7.0", "jotai": "^2.3.1", - "next": "13.4.6", - "postcss": "8.4.24", + "next": "^13.5.6", + "postcss": "^8.4.32", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.46.1", diff --git a/crates/apps/xeejp/pages/conduct.tsx b/crates/apps/xeejp/pages/conduct.tsx index e69de29..1c5da9f 100644 --- a/crates/apps/xeejp/pages/conduct.tsx +++ b/crates/apps/xeejp/pages/conduct.tsx @@ -0,0 +1,36 @@ +import { clientIdAtom, useUltimatumConductor } from "@/api/gameSocket"; +import { useAtom } from "jotai"; +import { atomWithStorage } from "jotai/utils"; +import { useRouter } from "next/router"; +import { v4 as uuidv4 } from "uuid"; + +const conductorPasswordAtom = atomWithStorage( + "conductorPassword", + uuidv4(), +); + +const Conduct = () => { + const router = useRouter(); + if (!router.isReady) return "loading"; + const { roomKey } = router.query; + return ; +}; + +const Inner = ({ roomKey }: { roomKey: string }) => { + const [channelId] = useAtom(clientIdAtom); + const [password] = useAtom(conductorPasswordAtom); + let { state, connectionStatus, sendCommand } = useUltimatumConductor( + roomKey, + channelId, + password, + ); + + return ( +
+
connectionStatus: {connectionStatus}
+
state: {JSON.stringify(state)}
+
+ ); +}; + +export default Conduct; diff --git a/crates/games/eagle-ultimatum/src/conductor_model.rs b/crates/games/eagle-ultimatum/src/conductor_model.rs index 8e4a3ad..02a25ef 100644 --- a/crates/games/eagle-ultimatum/src/conductor_model.rs +++ b/crates/games/eagle-ultimatum/src/conductor_model.rs @@ -1,14 +1,17 @@ use serde::{Deserialize, Serialize}; use tsify::Tsify; -use crate::types::{Proposal, Response}; +use crate::{ + phase::Phase, + types::{Proposal, Response}, +}; -#[derive(Tsify, Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Tsify, Debug, Default, Clone, PartialEq, Serialize, Deserialize)] pub struct UltimatumConductor { - proposal: Option, - proposed: bool, - response: Option, - errors: Vec, + pub phase: Phase, + pub proposal: Option, + pub response: Option, + pub errors: Vec, } impl UltimatumConductor { diff --git a/crates/games/eagle-ultimatum/src/game.rs b/crates/games/eagle-ultimatum/src/game.rs index e076a92..697c01d 100644 --- a/crates/games/eagle-ultimatum/src/game.rs +++ b/crates/games/eagle-ultimatum/src/game.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use eagle_game::prelude::*; +use eagle_game::{game::render_context::RenderContext, prelude::*}; use crate::{ conductor_model::UltimatumConductor, @@ -8,37 +8,19 @@ use crate::{ events::{UltimatumConductorCommand, UltimatumPlayerCommand}, phase::Phase, player_model::UltimatumPlayer, - types::{Players, Proposal, ProposalOpenTiming}, + types::{ControlVisibility, Proposal, ProposalOpenTiming, Response}, }; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct UltimatumGame { config: UltimatumConfig, phase: Phase, conductor: UltimatumConductor, - players: Map, -} - -pub(crate) fn standby(context: &mut impl GameContext, players: Players) -> Phase { - // context.push_player_notify(players.proposer, UltimatumPlayerNotify::YouAreProposer); - // context.push_player_notify(players.responder, UltimatumPlayerNotify::YouAreResponder); - return Phase::Standby { players }; -} - -pub(crate) fn open_proposal( - context: &mut impl GameContext, - players: Players, - proposal: Proposal, -) -> Phase { - // context.push_player_notify( - // players.proposer, - // UltimatumPlayerNotify::OpenProposal(proposal), - // ); - // context.push_player_notify( - // players.responder, - // UltimatumPlayerNotify::OpenProposal(proposal), - // ); - return Phase::Responding { players, proposal }; + proposer: Option, + responder: Option, + proposal: Option, + response: Option, + errors: Vec, } impl Game for UltimatumGame { @@ -49,15 +31,7 @@ impl Game for UltimatumGame { type PlayerView = UltimatumPlayer; fn new(config: Self::Config) -> Self { - Self { - config, - phase: Phase::WaitingForAttachment { - proposer: None, - responder: None, - }, - conductor: UltimatumConductor::new(), - players: Map::new(), - } + Self::default() } fn name() -> &'static str { @@ -66,61 +40,34 @@ impl Game for UltimatumGame { fn handle_conductor_command( &mut self, - context: &mut impl GameContext, + _context: &mut impl GameContext, command: Self::ConductorCommand, ) { use UltimatumConductorCommand as Command; match (&mut self.phase, command) { - (Phase::WaitingForAttachment { responder, .. }, Command::AttachProposer(proposer)) => { - if let Some(responder) = responder { - self.phase = standby( - context, - Players { - proposer, - responder: *responder, - }, - ) - } else { - self.phase = Phase::WaitingForAttachment { - proposer: Some(proposer), - responder: *responder, - } + (Phase::WaitingForAttachment, Command::AttachProposer(proposer)) => { + self.proposer = Some(proposer); + if self.responder.is_some() { + self.phase = Phase::Standby; } } - (Phase::WaitingForAttachment { proposer, .. }, Command::AttachResponder(responder)) => { - if let Some(proposer) = proposer { - self.phase = standby( - context, - Players { - proposer: *proposer, - responder, - }, - ) - } else { - self.phase = Phase::WaitingForAttachment { - proposer: *proposer, - responder: Some(responder), - } + (Phase::WaitingForAttachment, Command::AttachResponder(responder)) => { + self.responder = Some(responder); + if self.proposer.is_some() { + self.phase = Phase::Standby; } } - (Phase::Standby { players }, Command::StartGame) => { - // context.push_player_notify(players.proposer, UltimatumPlayerNotify::StartGame); - // context.push_player_notify(players.responder, UltimatumPlayerNotify::StartGame); - self.phase = Phase::Requesting { - players: *players, - proposal: None, - }; + (Phase::Standby, Command::StartGame) => { + self.phase = Phase::Proposing; } - (Phase::ProposalHidden { players, proposal }, Command::OpenProposal) => { - self.phase = open_proposal(context, *players, *proposal) + (Phase::ProposalHidden, Command::OpenProposal) => { + self.phase = Phase::Responding; } (phase, command) => { - // context.push_conductor_notify(UltimatumConductorNotify::Error( - // UltimatumError::UnexpectedConductorCommand { - // phase: phase.clone(), - // command, - // }, - // )) + self.errors.push(format!( + "Unexpected conductor command: {:?} in phase {:?}", + command, phase + )); } }; } @@ -133,47 +80,29 @@ impl Game for UltimatumGame { ) { use UltimatumPlayerCommand as Command; match (&mut self.phase, command) { - (Phase::Requesting { proposal, players }, Command::UpdateProposal(new)) => { - *proposal = Some(new); - // if self.config.control_visibility == ControlVisibility::Realtime { - // context.push_player_notify( - // players.responder, - // UltimatumPlayerNotify::UpdateProposal(*proposal), - // ); - // } + (Phase::Proposing, Command::UpdateProposal(new)) => { + self.proposal = Some(new); } - (Phase::Requesting { players, .. }, Command::SubmitProposal(proposal)) => { + (Phase::Proposing, Command::SubmitProposal(proposal)) => { + self.proposal = Some(proposal); match self.config.proposal_open_timing { ProposalOpenTiming::Immediate => { - self.phase = open_proposal(context, *players, proposal) + self.phase = Phase::Responding; } ProposalOpenTiming::ByConductor => { - self.phase = Phase::ProposalHidden { - players: *players, - proposal, - } + self.phase = Phase::ProposalHidden; } } } - (Phase::Responding { players, proposal }, Command::Respond(response)) => { - // context.push_player_notify( - // players.proposer, - // UltimatumPlayerNotify::Responded(response), - // ); - self.phase = Phase::Result { - players: *players, - proposal: *proposal, - response, - }; + (Phase::Responding, Command::Respond(response)) => { + self.response = Some(response); + self.phase = Phase::Result; } (phase, command) => { - // context.push_conductor_notify(UltimatumConductorNotify::Error( - // UltimatumError::UnexpectedPlayerCommand { - // phase: phase.clone(), - // player_id, - // command, - // }, - // )) + self.errors.push(format!( + "Unexpected player command: {:?} in phase {:?}", + command, phase + )); } } } @@ -183,21 +112,62 @@ impl Game for UltimatumGame { _context: &mut impl GameContext, _command: SystemCommand, ) { - todo!() + // Nothing for now } - fn render_conductor( - &self, - context: &impl eagle_game::game::render_context::RenderContext, - ) -> Self::ConductorView { - todo!() + fn render_conductor(&self, _context: &impl RenderContext) -> Self::ConductorView { + UltimatumConductor { + phase: self.phase.clone(), + proposal: self.proposal, + response: self.response, + errors: self.errors.clone(), + } } fn render_player( &self, - context: &impl eagle_game::game::render_context::RenderContext, + _context: &impl RenderContext, player_id: PlayerId, ) -> Self::PlayerView { - UltimatumPlayer {} + if self.proposer == Some(player_id) { + match self.phase { + Phase::WaitingForAttachment => UltimatumPlayer::Standby, + Phase::Standby => UltimatumPlayer::Standby, + Phase::Proposing => UltimatumPlayer::Proposing, + Phase::ProposalHidden => UltimatumPlayer::WaitingForResponse, + Phase::Responding => UltimatumPlayer::Responding { + proposal: self.proposal.unwrap(), + }, + Phase::Result => UltimatumPlayer::Result { + proposal: self.proposal.unwrap(), + response: self.response.unwrap(), + }, + } + } else if self.responder == Some(player_id) { + match self.phase { + Phase::WaitingForAttachment => UltimatumPlayer::Standby, + Phase::Standby => UltimatumPlayer::Standby, + Phase::Proposing => match self.config.control_visibility { + ControlVisibility::Realtime => UltimatumPlayer::WaitingForProposal { + realtime_proposal: self.proposal, + }, + ControlVisibility::Hidden => UltimatumPlayer::WaitingForProposal { + realtime_proposal: None, + }, + }, + Phase::ProposalHidden => UltimatumPlayer::WaitingForProposal { + realtime_proposal: None, + }, + Phase::Responding => UltimatumPlayer::Responding { + proposal: self.proposal.unwrap(), + }, + Phase::Result => UltimatumPlayer::Result { + proposal: self.proposal.unwrap(), + response: self.response.unwrap(), + }, + } + } else { + UltimatumPlayer::Standby + } } } diff --git a/crates/games/eagle-ultimatum/src/phase.rs b/crates/games/eagle-ultimatum/src/phase.rs index 82fd616..b73fc51 100644 --- a/crates/games/eagle-ultimatum/src/phase.rs +++ b/crates/games/eagle-ultimatum/src/phase.rs @@ -1,31 +1,12 @@ -use eagle_game::prelude::PlayerId; use serde::{Deserialize, Serialize}; -use crate::types::{Players, Proposal, Response}; - -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] pub enum Phase { - WaitingForAttachment { - proposer: Option, - responder: Option, - }, - /// Waiting for UltimatumConductor::StartGame - Standby { players: Players }, - Requesting { - players: Players, - proposal: Option, - }, - ProposalHidden { - players: Players, - proposal: Proposal, // 0 to 100 - }, - Responding { - players: Players, - proposal: Proposal, // 0 to 100 - }, - Result { - players: Players, - proposal: Proposal, - response: Response, - }, + #[default] + WaitingForAttachment, + Standby, + Proposing, + ProposalHidden, + Responding, + Result, } diff --git a/crates/games/eagle-ultimatum/src/player_model.rs b/crates/games/eagle-ultimatum/src/player_model.rs index 9e86169..7e777a6 100644 --- a/crates/games/eagle-ultimatum/src/player_model.rs +++ b/crates/games/eagle-ultimatum/src/player_model.rs @@ -1,5 +1,22 @@ use serde::{Deserialize, Serialize}; use tsify::Tsify; +use crate::types::{Proposal, Response}; + #[derive(Tsify, Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct UltimatumPlayer {} +pub enum UltimatumPlayer { + #[default] + Standby, + WaitingForProposal { + realtime_proposal: Option, + }, + Proposing, + WaitingForResponse, + Responding { + proposal: Proposal, + }, + Result { + proposal: Proposal, + response: Response, + }, +}