From 07295f3e96a16efc517e5a5e6ae0af4e3d5c5035 Mon Sep 17 00:00:00 2001 From: Joffrey Bion Date: Fri, 15 May 2020 03:05:09 +0200 Subject: Add dumb bots feature --- sw-bot/build.gradle.kts | 11 +++ .../org/luxons/sevenwonders/bot/SevenWondersBot.kt | 88 ++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 sw-bot/build.gradle.kts create mode 100644 sw-bot/src/main/kotlin/org/luxons/sevenwonders/bot/SevenWondersBot.kt (limited to 'sw-bot') diff --git a/sw-bot/build.gradle.kts b/sw-bot/build.gradle.kts new file mode 100644 index 00000000..fd4de5e0 --- /dev/null +++ b/sw-bot/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + kotlin("jvm") + id("org.jlleitschuh.gradle.ktlint") +} + +dependencies { + implementation(project(":sw-client")) + implementation(kotlin("stdlib-jdk8")) + testImplementation(kotlin("test")) + testImplementation(kotlin("test-junit")) +} diff --git a/sw-bot/src/main/kotlin/org/luxons/sevenwonders/bot/SevenWondersBot.kt b/sw-bot/src/main/kotlin/org/luxons/sevenwonders/bot/SevenWondersBot.kt new file mode 100644 index 00000000..deb7e237 --- /dev/null +++ b/sw-bot/src/main/kotlin/org/luxons/sevenwonders/bot/SevenWondersBot.kt @@ -0,0 +1,88 @@ +package org.luxons.sevenwonders.bot + +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeout +import org.luxons.sevenwonders.client.SevenWondersClient +import org.luxons.sevenwonders.client.SevenWondersSession +import org.luxons.sevenwonders.model.Action +import org.luxons.sevenwonders.model.MoveType +import org.luxons.sevenwonders.model.PlayerMove +import org.luxons.sevenwonders.model.PlayerTurnInfo +import org.luxons.sevenwonders.model.resources.noTransactions +import kotlin.random.Random +import kotlin.time.Duration +import kotlin.time.ExperimentalTime +import kotlin.time.hours + +@OptIn(ExperimentalTime::class) +data class BotConfig( + val minActionDelayMillis: Long = 500, + val maxActionDelayMillis: Long = 1000, + val globalTimeout: Duration = 10.hours +) + +@OptIn(ExperimentalTime::class) +class SevenWondersBot( + private val displayName: String, + private val botConfig: BotConfig = BotConfig() +) { + private val client = SevenWondersClient() + + suspend fun play(serverHost: String, gameId: Long) = withTimeout(botConfig.globalTimeout) { + val session = client.connect(serverHost) + botDelay() + session.chooseName(displayName) + botDelay() + session.joinGame(gameId) + botDelay() + session.awaitGameStart(gameId) + + coroutineScope { + launch { + session.watchTurns().first { turn -> + botDelay() + val keepPlaying = session.playTurn(turn) + !keepPlaying + } + } + botDelay() + session.sayReady() // triggers the first turn + } + session.disconnect() + } + + private suspend fun botDelay() { + delay(Random.nextLong(botConfig.minActionDelayMillis, botConfig.maxActionDelayMillis)) + } + + private suspend fun SevenWondersSession.playTurn(turn: PlayerTurnInfo): Boolean { + when (turn.action) { + Action.PLAY, Action.PLAY_2, Action.PLAY_LAST -> prepareMove(createPlayCardMove(turn)) + Action.PICK_NEIGHBOR_GUILD -> prepareMove(createPickGuildMove(turn)) + Action.SAY_READY -> sayReady() + Action.WAIT -> Unit + Action.WATCH_SCORE -> return false + } + return true + } +} + +private fun createPlayCardMove(turnInfo: PlayerTurnInfo): PlayerMove { + val wonderBuildability = turnInfo.wonderBuildability + if (wonderBuildability.isBuildable) { + val transactions = wonderBuildability.cheapestTransactions.first() + return PlayerMove(MoveType.UPGRADE_WONDER, turnInfo.hand!!.first().name, transactions) + } + val playableCard = turnInfo.hand!!.firstOrNull { it.playability.isPlayable } + return if (playableCard != null) { + PlayerMove(MoveType.PLAY, playableCard.name, playableCard.playability.cheapestTransactions.first()) + } else { + PlayerMove(MoveType.DISCARD, turnInfo.hand!!.first().name, noTransactions()) + } +} + +private fun createPickGuildMove(turnInfo: PlayerTurnInfo): PlayerMove = + PlayerMove(MoveType.COPY_GUILD, turnInfo.neighbourGuildCards.first().name) -- cgit