Files
games/src/renaissance.ts
2025-08-02 00:01:27 -04:00

83 lines
2.0 KiB
TypeScript

import { Board, Card, Hand, Pile, Stack, Suit } from "./types/cards";
import { clone } from "./fn";
const AGG: Suit = "spades";
const CUL: Suit = "hearts";
const TECH: Suit = "diamonds";
const MIL: Suit = "clubs";
type GameState = {
mainPile: Pile;
discardStack: Stack;
deadPile: Pile;
players: {
hand: Hand;
board: Board;
}[];
playerTurn: number;
lastMove: Move;
prevState: GameState | null;
};
type Move =
| {
type: "draw";
fromPile: "main" | "discard";
}
| { type: "play"; card: Card }
| { type: "remove"; card: Card }
| { type: "attack"; army: Card[] }
| { type: "pass" };
type Action = Move | { type: "discard"; cards: Card[] };
const techTier = (techValue: number) => Math.ceil(techValue / 14);
const moveCost = (move: Move) => (move.type == "attack" ? move.army.length : 1);
const movesSpent = (state: GameState): number =>
state.playerTurn != state.prevState?.playerTurn
? 0
: moveCost(state.lastMove) + movesSpent(state.prevState);
const value = (suit: Suit) => (board: Board) =>
board
.filter((card) => card.suit === suit)
.reduce((v, card) => v + card.value, 0);
export const getNextState = (p: {
state: GameState;
move: Move;
}): GameState | { illegal: string } => {
const { state, move } = p;
const currentPlayer = state.players[state.playerTurn];
const population = currentPlayer.board.thru(value(AGG));
const culture = currentPlayer.board.thru(value(CUL));
const tech = techTier(currentPlayer.board.thru(value(TECH)));
const movesRemaining = tech - movesSpent(state);
const newState = clone(state);
if (move.type === "draw") {
const pile =
move.fromPile === "main"
? newState.mainPile
: newState.discardStack;
if (pile.length === 0) {
return {
illegal: `The ${move.fromPile} pile is empty; cannot draw`,
};
}
newState.players[newState.playerTurn].hand.push(pile.pop()!);
if (movesRemaining == 1) {
newState.playerTurn =
(newState.playerTurn + 1) % newState.players.length;
}
return newState;
}
return { illegal: "idk bruh" };
};