Skip to content

Commit

Permalink
feat: implement separate simple and advanced player
Browse files Browse the repository at this point in the history
  • Loading branch information
xeruf committed Jul 17, 2024
1 parent 6db9922 commit fe49e03
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 12 deletions.
26 changes: 23 additions & 3 deletions src/main/kotlin/sc/gui/controller/ClientController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sc.gui.controller

import sc.api.plugins.IGameState
import sc.api.plugins.IMove
import sc.api.plugins.SENSIBLE_MOVES_COUNT
import sc.api.plugins.TwoPlayerGameState
import sc.api.plugins.exceptions.GameLogicException
import sc.gui.LobbyManager
Expand All @@ -15,6 +16,7 @@ import sc.gui.serverAddress
import sc.gui.serverPort
import tornadofx.*
import java.util.concurrent.CompletableFuture
import kotlin.random.Random

data class StartGame(val settings: List<TeamSettings>): FXEvent()
class HumanMoveRequest: FXEvent()
Expand All @@ -36,10 +38,10 @@ class ClientController: Controller() {
val players = playerSettings.map { teamSettings ->
Player(teamSettings.name.get(), when (val type = teamSettings.type.value) {
PlayerType.HUMAN -> GuiClient(host, port, type, ::humanMoveRequest)
PlayerType.COMPUTER_EXAMPLE -> GuiClient(host, port, type, ::getSimpleMove)
PlayerType.COMPUTER_SIMPLE -> GuiClient(host, port, type, ::getSimpleMove)
PlayerType.COMPUTER_ADVANCED -> GuiClient(host, port, type, ::getAdvancedMove)
PlayerType.COMPUTER -> ExecClient(host, port, teamSettings.executable.get())
PlayerType.EXTERNAL -> ExternalClient(host, port)
else -> throw IllegalArgumentException("Cannot create game: Invalid playerType $type")
})
}
// TODO handle client start failures
Expand All @@ -56,14 +58,32 @@ class ClientController: Controller() {
return future
}

val random = Random

/** Reservoir sampling.
* https://math.stackexchange.com/questions/1058500/can-you-select-random-entry-from-unknown-number-of-entries/1058547#1058547 */
fun getSimpleMove(state: IGameState): CompletableFuture<IMove> {
val possibleMoves = state.moveIterator()
var selection = possibleMoves.next()
var count = 1
while(possibleMoves.hasNext()) {
count++
val next = possibleMoves.next()
if(random.nextInt(count) == 0)
selection = next
}
return CompletableFuture.completedFuture(selection)
}

/** Evaluation of following state. */
fun getAdvancedMove(state: IGameState): CompletableFuture<IMove> {
val possibleMoves = state.moveIterator()
if (!possibleMoves.hasNext())
throw GameLogicException("No possible Moves found!")
val best = ArrayList<IMove>()
var bestValue = Integer.MIN_VALUE
var count = 0
while(possibleMoves.hasNext() && count < 64) {
while(possibleMoves.hasNext() && count < SENSIBLE_MOVES_COUNT) {
val next = possibleMoves.next()
@Suppress("UNCHECKED_CAST")
val newState = (state as TwoPlayerGameState<IMove>).performMove(next)
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/sc/gui/model/GameCreationModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import java.io.File

enum class PlayerType(val description: String) {
HUMAN("Mensch"),
COMPUTER_EXAMPLE("Beispiel-Computerspieler"),
COMPUTER_SIMPLE("Zufalls-Computerspieler"),
COMPUTER_ADVANCED("Fortgeschrittener Computerspieler"),
COMPUTER("Eigener Computerspieler, von GUI gestartet"),
EXTERNAL("Eigener Computerspieler, manuell gestartet");
override fun toString() = description
companion object {
/** Helper to disable human player until ready. */
fun allowedValues() = values() //.takeLast(3)
fun allowedValues() = entries //.takeLast(3)
}
}

Expand Down
15 changes: 8 additions & 7 deletions src/main/kotlin/sc/gui/view/GameCreationView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import java.io.File

class GameCreationView: View() {
private val playerSettingsModels =
arrayOf(TeamSettings("Spieler 1", PlayerType.COMPUTER_EXAMPLE),
arrayOf(TeamSettings("Spieler 1", PlayerType.COMPUTER_SIMPLE),
TeamSettings("Spieler 2", PlayerType.allowedValues().first()))
.map { TeamSettingsModel(it) }

Expand Down Expand Up @@ -84,6 +84,7 @@ class PlayerFileSelectFragment(private val team: Team, private val settings: Tea

private fun updatePlayerType() {
// TODO: work with proper binding of property
root.bottom = label("")
when(settings.type.value as PlayerType) {
PlayerType.COMPUTER -> {
root.center = hbox(AppStyle.spacing) {
Expand All @@ -109,16 +110,16 @@ class PlayerFileSelectFragment(private val team: Team, private val settings: Tea
}
}
PlayerType.EXTERNAL -> {
root.center = label("Spieler muss nach Erstellung des Spiels separat gestartet werden")
root.bottom = label("")
root.center = label("Spieler nach Erstellung des Spiels separat starten")
}
PlayerType.COMPUTER_EXAMPLE -> {
root.center = label("Ein einfacher, interner Computerspieler")
root.bottom = label()
PlayerType.COMPUTER_SIMPLE -> {
root.center = label("Ein einfacher, eingebauter Computerspieler")
}
PlayerType.COMPUTER_ADVANCED -> {
root.center = label("Ein forgeschrittener, eingebauter Computerspieler")
}
PlayerType.HUMAN -> {
root.center = label("Ein von Hand gesteuerter Spieler")
root.bottom = label()
}
}
(root.center as Region).paddingTop = AppStyle.formSpacing
Expand Down

0 comments on commit fe49e03

Please sign in to comment.