refactor: add rng & seeding
This commit is contained in:
@@ -2,7 +2,8 @@ import {
|
||||
OnitamaGame,
|
||||
OnitamaState,
|
||||
PlayerType,
|
||||
prompts
|
||||
prompts,
|
||||
initializeCards
|
||||
} from "./types";
|
||||
import {createGameCommandRegistry} from "@/core/game";
|
||||
import {moveToRegion} from "@/core/region";
|
||||
@@ -342,14 +343,17 @@ const turn = registry.register({
|
||||
* 开始游戏主循环
|
||||
*/
|
||||
export async function start(game: OnitamaGame) {
|
||||
// Initialize cards with RNG at game start
|
||||
initializeCards(game);
|
||||
|
||||
while (true) {
|
||||
const currentPlayer = game.value.currentPlayer;
|
||||
const turnOutput = await turn(game, currentPlayer);
|
||||
|
||||
|
||||
if (turnOutput.winner) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return game.value;
|
||||
}
|
||||
|
||||
@@ -125,27 +125,13 @@ export function createInitialState() {
|
||||
const regions = createRegions();
|
||||
const pawns = createPawns();
|
||||
const cards = createCards();
|
||||
|
||||
// Distribute cards: 2 to each player, 1 spare
|
||||
const cardNames = Object.keys(cards);
|
||||
const shuffled = [...cardNames].sort(() => Math.random() - 0.5);
|
||||
|
||||
const redCards = shuffled.slice(0, 2);
|
||||
const blackCards = shuffled.slice(2, 4);
|
||||
const spareCard = shuffled[4];
|
||||
|
||||
// Set card regions
|
||||
for(const cardName of redCards){
|
||||
cards[cardName].regionId = 'red';
|
||||
regions.red.childIds.push(cardName);
|
||||
}
|
||||
for(const cardName of blackCards){
|
||||
cards[cardName].regionId = 'black';
|
||||
regions.black.childIds.push(cardName);
|
||||
}
|
||||
cards[spareCard].regionId = 'spare';
|
||||
regions.spare.childIds.push(spareCard);
|
||||
|
||||
|
||||
// Cards will be shuffled and distributed in start()
|
||||
// Initialize with empty card lists
|
||||
const redCards: string[] = [];
|
||||
const blackCards: string[] = [];
|
||||
const spareCard = '';
|
||||
|
||||
// Populate board region childIds
|
||||
for(const pawn of Object.values(pawns)){
|
||||
if(pawn.regionId === 'board'){
|
||||
@@ -153,15 +139,12 @@ export function createInitialState() {
|
||||
regions.board.partMap[pawn.position.join(',')] = pawn.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine starting player from spare card
|
||||
const startingPlayer = cards[spareCard].startingPlayer;
|
||||
|
||||
|
||||
return {
|
||||
regions,
|
||||
pawns,
|
||||
cards,
|
||||
currentPlayer: startingPlayer,
|
||||
currentPlayer: 'red' as PlayerType,
|
||||
winner: null as PlayerType | null,
|
||||
spareCard,
|
||||
redCards,
|
||||
@@ -170,5 +153,47 @@ export function createInitialState() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 洗牌并分配卡牌,设置起始玩家
|
||||
*/
|
||||
export function initializeCards(game: OnitamaGame) {
|
||||
const state = game.value;
|
||||
const rng = game.rng;
|
||||
const cardNames = Object.keys(state.cards);
|
||||
|
||||
// Fisher-Yates shuffle using game.rng
|
||||
const shuffled = [...cardNames];
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = rng.nextInt(i + 1);
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||
}
|
||||
|
||||
const redCards = shuffled.slice(0, 2);
|
||||
const blackCards = shuffled.slice(2, 4);
|
||||
const spareCard = shuffled[4];
|
||||
|
||||
game.produce(state => {
|
||||
// Set card regions
|
||||
for(const cardName of redCards){
|
||||
state.cards[cardName].regionId = 'red';
|
||||
state.regions.red.childIds.push(cardName);
|
||||
}
|
||||
for(const cardName of blackCards){
|
||||
state.cards[cardName].regionId = 'black';
|
||||
state.regions.black.childIds.push(cardName);
|
||||
}
|
||||
state.cards[spareCard].regionId = 'spare';
|
||||
state.regions.spare.childIds.push(spareCard);
|
||||
|
||||
// Set card lists
|
||||
state.redCards = redCards;
|
||||
state.blackCards = blackCards;
|
||||
state.spareCard = spareCard;
|
||||
|
||||
// Determine starting player from spare card
|
||||
state.currentPlayer = state.cards[spareCard].startingPlayer;
|
||||
});
|
||||
}
|
||||
|
||||
export type OnitamaState = ReturnType<typeof createInitialState>;
|
||||
export type OnitamaGame = IGameContext<OnitamaState>;
|
||||
Reference in New Issue
Block a user