Skip to content

Commit

Permalink
bot actions
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanseifert committed Oct 19, 2024
1 parent ab267e6 commit a08fd68
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 6 deletions.
142 changes: 142 additions & 0 deletions src/services/BotActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import Card, { DiceAction, ScoringAction } from './Card'
import Action from './enum/Action'
import DifficultyLevel from './enum/DifficultyLevel'
import ScoringActionType from './enum/ScoringActionType'
import ScoringCategory from './enum/ScoringCategory'
import NavigationState from '@/util/NavigationState'

/**
* Manages the bot actions.
*/
export default class BotActions {

readonly _items : ActionItem[]
readonly _reset : boolean

public constructor(currentCard: Card, navigationState: NavigationState) {
const { blueDotCount, redDotCount, round, difficultyLevel,
eraScoringTiles, finalScoringTiles, actionRoll } = navigationState

// check reset
this._reset = (blueDotCount >= 4 && redDotCount >= 4)

// collect actions
if (!this.isReset && currentCard.diceActions && currentCard.diceActionsAdvanced) {
if (isAdvanced(round, difficultyLevel)) {
this._items = getDiceActions(currentCard.diceActionsAdvanced, actionRoll)
}
else {
this._items = getDiceActions(currentCard.diceActions, actionRoll)
}
}
else if (!this.isReset && currentCard.scoringAction && currentCard.scoringActionAdvanced) {
if (isAdvanced(round, difficultyLevel)) {
this._items = getScoringActions(currentCard.scoringActionAdvanced, round, eraScoringTiles, finalScoringTiles)
}
else {
this._items = getScoringActions(currentCard.scoringAction, round, eraScoringTiles, finalScoringTiles)
}
}
else {
this._items = []
}
}

public get items() : readonly ActionItem[] {
return this._items
}

public get isReset() : boolean {
return this._reset
}

}

export interface ActionItem {
action: Action
scoringCategory?: ScoringCategory
count?: number
}

function isAdvanced(round: number, difficultyLevel: DifficultyLevel) : boolean {
switch (difficultyLevel) {
case DifficultyLevel.BEGINNER:
return false
case DifficultyLevel.MODERATE:
return round >= 4
case DifficultyLevel.MEDIUM:
return round >= 3
case DifficultyLevel.ADVANCED:
return round >= 2
case DifficultyLevel.EXPERT:
return true
default:
throw new Error(`Invalid difficulty level: ${difficultyLevel}`)
}
}

function getDiceActions(diceActions: DiceAction[], actionRoll: number) : ActionItem[] {
return diceActions.
filter(item => item.values.includes(actionRoll))
.map(item => {
const { action, scoringCategory, count } = item
const actionItem : ActionItem = { action }
if (scoringCategory) {
actionItem.scoringCategory = scoringCategory
}
if (count) {
actionItem.count = count
}
return actionItem
})
}

function getScoringActions(scoringAction: ScoringAction, round: number,
eraScoringTiles: ScoringCategory[], finalScoringTiles: ScoringCategory[]) : ActionItem[] {
const { scoringActionType, count, vpCount, alternativeLastEraVPCount } = scoringAction
switch (scoringActionType) {
case ScoringActionType.CURRENT_ERA_SCORING_CATEGORY:
return getEraScoringCategoryActions(getEraScoringCategory(eraScoringTiles, round), count, vpCount)
case ScoringActionType.NEXT_ERA_SCORING_CATEGORY:
if (round < 4) {
return getEraScoringCategoryActions(getEraScoringCategory(eraScoringTiles, round+1), count, vpCount)
}
else {
return [{ action:Action.GAIN_VP, count:alternativeLastEraVPCount }]
}
case ScoringActionType.LAST_ERA_SCORING_CATEGORY:
return getEraScoringCategoryActions(getEraScoringCategory(eraScoringTiles, 4), count, vpCount)
case ScoringActionType.FINAL_SCORING_CATEGORIES:
return getFinalScoringCategoryActions(finalScoringTiles, count, vpCount)
default:
throw new Error(`Invalid scoring action: ${scoringAction}`)
}
}

function getEraScoringCategory(eraScoringTiles: ScoringCategory[], round:number) : ScoringCategory {
const scoringCategory = eraScoringTiles[round-1]
if (!scoringCategory) {
throw new Error(`Invalid era scoring category: ${round}`)
}
return scoringCategory
}

function getEraScoringCategoryActions(scoringCategory: ScoringCategory, count: number, vpCount?: number) : ActionItem[] {
const items : ActionItem[] = []
items.push({ action:Action.ADVANCE_SCORING_CATEGORY, scoringCategory, count })
if (vpCount) {
items.push({ action:Action.GAIN_VP, count:vpCount })
}
return items
}

function getFinalScoringCategoryActions(scoringCategories: ScoringCategory[], count: number, vpCount?: number) : ActionItem[] {
const items : ActionItem[] = []
scoringCategories.forEach(scoringCategory => {
items.push({ action:Action.ADVANCE_SCORING_CATEGORY, scoringCategory, count })
})
if (vpCount) {
items.push({ action:Action.GAIN_VP, count:vpCount })
}
return items
}
2 changes: 2 additions & 0 deletions src/store/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export interface BotPersistence {
cardDeck: CardDeckPersistence
evolutionCount: number
prosperityCount: number
blueDotCount: number
redDotCount: number
actionRoll: number
territoryRoll: number
beaconRoll: number
Expand Down
34 changes: 31 additions & 3 deletions src/util/NavigationState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Card from '@/services/Card'
import Cards from '@/services/Cards'
import rollDice from '@brdgm/brdgm-commons/src/util/random/rollDice'
import Player from '@/services/enum/Player'
import DifficultyLevel from '@/services/enum/DifficultyLevel'
import ScoringCategory from '@/services/enum/ScoringCategory'

export default class NavigationState {

Expand All @@ -14,13 +16,19 @@ export default class NavigationState {
readonly startPlayer : Player
readonly player : Player

readonly difficultyLevel : DifficultyLevel
readonly eraScoringTiles : ScoringCategory[]
readonly finalScoringTiles : ScoringCategory[]

readonly cardDeck : CardDeck
readonly evolutionCount : number
readonly prosperityCount : number
readonly currentCard : Card
readonly actionRoll : number
readonly territoryRoll : number
readonly beaconRoll : number
readonly blueDotCount : number
readonly redDotCount : number

constructor(route: RouteLocation, state: State) {
this.round = getIntRouteParam(route, 'round')
Expand All @@ -34,12 +42,18 @@ export default class NavigationState {
this.startPlayer = getStartPlayer(state, this.round)
this.player = getPlayer(route, this.startPlayer)

this.difficultyLevel = state.setup.difficultyLevel
this.eraScoringTiles = state.setup.eraScoringTiles
this.finalScoringTiles = state.setup.finalScoringTiles

// try to load persistence with rolled die values for current turns
const botPersistence = getBotPersistence(state, this.round, this.turn)
if (botPersistence) {
this.cardDeck = CardDeck.fromPersistence(botPersistence.cardDeck)
this.evolutionCount = botPersistence.evolutionCount
this.prosperityCount = botPersistence.prosperityCount
this.blueDotCount = botPersistence.blueDotCount
this.redDotCount = botPersistence.redDotCount
this.actionRoll = botPersistence.actionRoll
this.territoryRoll = botPersistence.territoryRoll
this.beaconRoll = botPersistence.beaconRoll
Expand All @@ -48,9 +62,15 @@ export default class NavigationState {
else {
const previousBotPersistence = getPreviousBotPersistence(state, this.round, this.turn)
this.cardDeck = CardDeck.fromPersistence(previousBotPersistence.cardDeck)
// draw next card
// draw next card, count dots
if (this.player == Player.BOT) {
this.cardDeck.draw()
const nextCard = this.cardDeck.draw()
this.blueDotCount = previousBotPersistence.blueDotCount + nextCard.blueDotCount
this.redDotCount = previousBotPersistence.redDotCount + nextCard.redDotCount
}
else {
this.blueDotCount = previousBotPersistence.blueDotCount
this.redDotCount = previousBotPersistence.redDotCount
}
// counters
this.evolutionCount = previousBotPersistence.evolutionCount
Expand Down Expand Up @@ -118,7 +138,13 @@ function getPreviousBotPersistence(state: State, round: number, turn: number) :

// check previous round
if (round > 1) {
return getPreviousBotPersistence(state, round - 1, MAX_TURN)
const lastRoundBotPersistence = getPreviousBotPersistence(state, round - 1, MAX_TURN)
if (lastRoundBotPersistence) {
// reset dot counters for new round
lastRoundBotPersistence.blueDotCount = 0
lastRoundBotPersistence.redDotCount = 0
return lastRoundBotPersistence
}
}

// get initial card deck
Expand All @@ -130,6 +156,8 @@ function getPreviousBotPersistence(state: State, round: number, turn: number) :
cardDeck: initialCardDeck,
evolutionCount: 0,
prosperityCount: 0,
blueDotCount: 0,
redDotCount: 0,
actionRoll: 0,
territoryRoll: 0,
beaconRoll: 0
Expand Down
5 changes: 4 additions & 1 deletion src/views/TurnBot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export default defineComponent({
},
methods: {
saveTurn() : void {
const { player, cardDeck, evolutionCount, prosperityCount, actionRoll, territoryRoll, beaconRoll } = this.navigationState
const { player, cardDeck, evolutionCount, prosperityCount, blueDotCount, redDotCount,
actionRoll, territoryRoll, beaconRoll } = this.navigationState
const turn : Turn = {
round: this.round,
turn: this.turn,
Expand All @@ -60,6 +61,8 @@ export default defineComponent({
cardDeck: cardDeck.toPersistence(),
evolutionCount,
prosperityCount,
blueDotCount,
redDotCount,
actionRoll,
territoryRoll,
beaconRoll
Expand Down
8 changes: 7 additions & 1 deletion tests/unit/helper/mockTurn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ export default function (params?: MockTurnParams) : Turn {
turn: params?.turn ?? 1,
player: params?.player ?? Player.PLAYER
}
if (params?.cardDeck || params?.evolutionCount || params?.prosperityCount || params?.actionRoll || params?.territoryRoll || params?.beaconRoll) {
if (params?.cardDeck || params?.evolutionCount || params?.prosperityCount
|| params?.actionRoll || params?.territoryRoll || params?.beaconRoll
|| params?.blueDotCount || params?.redDotCount) {
turn.botPersistence = {
cardDeck: params?.cardDeck ?? CardDeck.new().toPersistence(),
evolutionCount: params?.evolutionCount ?? 0,
prosperityCount: params?.prosperityCount ?? 0,
blueDotCount: params?.blueDotCount ?? 0,
redDotCount: params?.redDotCount ?? 0,
actionRoll: params?.actionRoll ?? 0,
territoryRoll: params?.territoryRoll ?? 0,
beaconRoll: params?.beaconRoll ?? 0
Expand All @@ -28,6 +32,8 @@ export interface MockTurnParams {
cardDeck?: CardDeckPersistence
evolutionCount?: number
prosperityCount?: number
blueDotCount?: number
redDotCount?: number
actionRoll?: number
territoryRoll?: number
beaconRoll?: number
Expand Down
Loading

0 comments on commit a08fd68

Please sign in to comment.