mirror of
https://github.com/Vale54321/schafkopf-bot.git
synced 2025-12-16 11:49:33 +01:00
added cmd playing mode
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
package de.heiserer
|
||||
|
||||
class SchafkopfGameController {
|
||||
private val players = listOf(NPCPlayer("NPC 1"), NPCPlayer("NPC 2"), NPCPlayer("NPC 3"), NPCPlayer("NPC 4"))
|
||||
import de.heiserer.cards.*
|
||||
import de.heiserer.player.Player
|
||||
|
||||
class SchafkopfGameController(palyers: List<Player>, private val messager: SchafkopfMessager) {
|
||||
private val players = palyers
|
||||
|
||||
private lateinit var gameType: GameType
|
||||
private val playedCards: CardList = UnsortedCardList()
|
||||
@@ -16,6 +19,7 @@ class SchafkopfGameController {
|
||||
|
||||
for(i in 0 until 8){
|
||||
startingPlayer = playTrick(startingPlayer)
|
||||
messager.sendPlayerWonTrick(startingPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +28,17 @@ class SchafkopfGameController {
|
||||
|
||||
for(i in 0 until 4){
|
||||
val currentPlayer = calculatePlayerOffset(startingPlayer, i)
|
||||
currentPlayer.printName()
|
||||
|
||||
messager.sendPlayerTurn(currentPlayer)
|
||||
val card = currentPlayer.playCard(tableCards, gameType)
|
||||
messager.sendCardPlayed(currentPlayer, card)
|
||||
tableCards.add(card)
|
||||
messager.sendTableCards(tableCards.getCopyOfCards())
|
||||
}
|
||||
|
||||
tableCards.print()
|
||||
playedCards.add(tableCards)
|
||||
|
||||
return startingPlayer
|
||||
val trickOffset = CardToolkit.whoTricks(gameType, tableCards)
|
||||
return calculatePlayerOffset(startingPlayer, trickOffset)
|
||||
}
|
||||
|
||||
private fun calculatePlayerOffset(startingPlayer: Player, i: Int): Player = players[(players.indexOf(startingPlayer) + i) % 4]
|
||||
@@ -52,62 +58,3 @@ class SchafkopfGameController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Player(private var name: String){
|
||||
protected var cards: SortedCardList = SortedCardList(GameType.SAU_SPIEL)
|
||||
|
||||
fun serveCards(cards: UnsortedCardList){
|
||||
this.cards = cards.asSortedCardList(GameType.SAU_SPIEL)
|
||||
}
|
||||
|
||||
fun sortCards(gameType: GameType){
|
||||
cards = cards.asSortedCardList(gameType)
|
||||
println("Spieler $name hat folgende Karten sortiert:")
|
||||
cards.print()
|
||||
println()
|
||||
println("Trumpf:")
|
||||
cards.getTrumpf().print()
|
||||
println()
|
||||
println("Farbe:")
|
||||
cards.getCardsWithoutTrumpf().print()
|
||||
println()
|
||||
}
|
||||
|
||||
abstract fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card
|
||||
|
||||
fun printName(){
|
||||
println(name)
|
||||
}
|
||||
}
|
||||
|
||||
class NPCPlayer(name: String) : Player(name){
|
||||
override fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card {
|
||||
if(tableCards.size() == 0){
|
||||
println("Erster Spieler")
|
||||
val card = cards.removeLast()
|
||||
println("Spielt ${card.displayName}")
|
||||
println()
|
||||
return card
|
||||
}
|
||||
|
||||
val firstCard = tableCards.get(0)
|
||||
if(cards.farbFreiOrTrumpf(firstCard, gameType)){
|
||||
println("farbFrei Or Trumpf")
|
||||
return playTrumpf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun playTrumpf(): Card {
|
||||
val trumpf = cards.getTrumpf()
|
||||
if (trumpf.size() > 0) {
|
||||
println("play Trumpf")
|
||||
val card = trumpf.removeLast()
|
||||
println("Spielt ${card.displayName}")
|
||||
println()
|
||||
return cards.remove(card)
|
||||
} else {
|
||||
println("abspatzen")
|
||||
return cards.removeFirst()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.heiserer
|
||||
|
||||
import de.heiserer.cards.Card
|
||||
import de.heiserer.player.Player
|
||||
|
||||
interface SchafkopfMessager {
|
||||
fun sendPlayerTurn(player: Player)
|
||||
fun sendPlayerWonTrick(player: Player)
|
||||
fun sendPlayerWonGame(player: Player)
|
||||
fun sendCardPlayed(player: Player, card: Card)
|
||||
fun sendTableCards(cards: List<Card>)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.heiserer.cards
|
||||
|
||||
class CardAlreadyAddedException(message: String, val card: Card) : RuntimeException(message) {
|
||||
|
||||
// You can add additional constructors or methods if needed, but for now, this is sufficient to handle the scenario of adding a card that's already in the list.
|
||||
}
|
||||
21
Backend/schafkopf-shared/src/main/kotlin/card/CardList.kt
Normal file
21
Backend/schafkopf-shared/src/main/kotlin/card/CardList.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package de.heiserer.cards
|
||||
|
||||
interface CardList {
|
||||
fun add(card: Card)
|
||||
fun add(cards: CardList)
|
||||
|
||||
fun remove(card: Card): Card
|
||||
fun remove(cards: CardList)
|
||||
fun removeLast(): Card
|
||||
fun removeFirst(): Card
|
||||
|
||||
fun get(index: Int): Card
|
||||
fun getLast(): Card
|
||||
fun getCopyOfCards(): List<Card>
|
||||
|
||||
fun indexOf(card: Card): Int
|
||||
operator fun contains(card: Card): Boolean
|
||||
fun size(): Int
|
||||
fun print()
|
||||
fun asSortedCardList(type: GameType): SortedCardList
|
||||
}
|
||||
36
Backend/schafkopf-shared/src/main/kotlin/card/CardToolkit.kt
Normal file
36
Backend/schafkopf-shared/src/main/kotlin/card/CardToolkit.kt
Normal file
@@ -0,0 +1,36 @@
|
||||
package de.heiserer.cards
|
||||
|
||||
class CardToolkit private constructor(private val gameType: GameType) {
|
||||
private val sortedCardList = UnsortedCardList(true).asSortedCardList(gameType)
|
||||
|
||||
fun isTrumpf(card: Card): Boolean = card in sortedCardList.getTrumpf().getCopyOfCards()
|
||||
|
||||
fun whoTricks(cards: CardList): Int {
|
||||
if(cards.size() != 4){
|
||||
throw IllegalArgumentException("Es müssen 4 Karten auf dem Tisch liegen.")
|
||||
}
|
||||
|
||||
val sortedCards = cards.asSortedCardList(gameType)
|
||||
|
||||
if(sortedCards.getTrumpf().size() > 0){
|
||||
return cards.indexOf(sortedCards.getTrumpf().getLast())
|
||||
} else {
|
||||
val firstColor = cards.get(0).color
|
||||
val colorCards = sortedCards.getCardsWithoutTrumpf(firstColor)
|
||||
|
||||
return cards.indexOf(colorCards.getLast())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isTrumpf(gameType: GameType, card: Card): Boolean {
|
||||
val toolkit = CardToolkit(gameType)
|
||||
return toolkit.isTrumpf(card)
|
||||
}
|
||||
|
||||
fun whoTricks(gameType: GameType, cards: CardList): Int {
|
||||
val toolkit = CardToolkit(gameType)
|
||||
return toolkit.whoTricks(cards)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package de.heiserer
|
||||
package de.heiserer.cards
|
||||
|
||||
enum class Card(val color: CardColor, val symbol: CardSymbol) {
|
||||
SCHELL_7(CardColor.SCHELL, CardSymbol.SIEBEN),
|
||||
SCHELL_8(CardColor.SCHELL, CardSymbol.ACHT),
|
||||
SCHELL_9(CardColor.SCHELL, CardSymbol.NINE),
|
||||
SCHELL_9(CardColor.SCHELL, CardSymbol.NEUN),
|
||||
SCHELL_U(CardColor.SCHELL, CardSymbol.UNTER),
|
||||
SCHELL_O(CardColor.SCHELL, CardSymbol.OBER),
|
||||
SCHELL_K(CardColor.SCHELL, CardSymbol.KOENIG),
|
||||
@@ -11,7 +11,7 @@ enum class Card(val color: CardColor, val symbol: CardSymbol) {
|
||||
SCHELL_A(CardColor.SCHELL, CardSymbol.ASS),
|
||||
HERZ_7(CardColor.HERZ, CardSymbol.SIEBEN),
|
||||
HERZ_8(CardColor.HERZ, CardSymbol.ACHT),
|
||||
HERZ_9(CardColor.HERZ, CardSymbol.NINE),
|
||||
HERZ_9(CardColor.HERZ, CardSymbol.NEUN),
|
||||
HERZ_U(CardColor.HERZ, CardSymbol.UNTER),
|
||||
HERZ_O(CardColor.HERZ, CardSymbol.OBER),
|
||||
HERZ_K(CardColor.HERZ, CardSymbol.KOENIG),
|
||||
@@ -19,7 +19,7 @@ enum class Card(val color: CardColor, val symbol: CardSymbol) {
|
||||
HERZ_A(CardColor.HERZ, CardSymbol.ASS),
|
||||
BLATT_7(CardColor.BLATT, CardSymbol.SIEBEN),
|
||||
BLATT_8(CardColor.BLATT, CardSymbol.ACHT),
|
||||
BLATT_9(CardColor.BLATT, CardSymbol.NINE),
|
||||
BLATT_9(CardColor.BLATT, CardSymbol.NEUN),
|
||||
BLATT_U(CardColor.BLATT, CardSymbol.UNTER),
|
||||
BLATT_O(CardColor.BLATT, CardSymbol.OBER),
|
||||
BLATT_K(CardColor.BLATT, CardSymbol.KOENIG),
|
||||
@@ -27,7 +27,7 @@ enum class Card(val color: CardColor, val symbol: CardSymbol) {
|
||||
BLATT_A(CardColor.BLATT, CardSymbol.ASS),
|
||||
EICHEL_7(CardColor.EICHEL, CardSymbol.SIEBEN),
|
||||
EICHEL_8(CardColor.EICHEL, CardSymbol.ACHT),
|
||||
EICHEL_9(CardColor.EICHEL, CardSymbol.NINE),
|
||||
EICHEL_9(CardColor.EICHEL, CardSymbol.NEUN),
|
||||
EICHEL_U(CardColor.EICHEL, CardSymbol.UNTER),
|
||||
EICHEL_O(CardColor.EICHEL, CardSymbol.OBER),
|
||||
EICHEL_K(CardColor.EICHEL, CardSymbol.KOENIG),
|
||||
@@ -49,7 +49,7 @@ enum class CardColor(val order: Int, val displayName: String) {
|
||||
enum class CardSymbol(val order: Int, val displayName: String, val value: Int) {
|
||||
SIEBEN(0,"7", 0),
|
||||
ACHT(1,"8", 0),
|
||||
NINE(2,"9", 0),
|
||||
NEUN(2,"9", 0),
|
||||
UNTER(3,"Unter", 2),
|
||||
OBER(4,"Ober", 3),
|
||||
KOENIG(5,"König", 4),
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.heiserer.cards
|
||||
|
||||
class SortedCardList(private val gameType: GameType, withAllCards: Boolean = false) : UnsortedCardList(withAllCards) {
|
||||
override fun add(card: Card) {
|
||||
super.add(card)
|
||||
sort()
|
||||
}
|
||||
|
||||
fun getCardsWithoutTrumpf(color: CardColor? = null): SortedCardList {
|
||||
val cardsWithoutTrumpf = SortedCardList(gameType)
|
||||
|
||||
color?.let { cardsWithoutTrumpf.add(get(it)) }?: cardsWithoutTrumpf.add(this)
|
||||
try {
|
||||
cardsWithoutTrumpf.remove(getTrumpf())
|
||||
} catch (_: IllegalArgumentException) {
|
||||
}
|
||||
|
||||
return cardsWithoutTrumpf
|
||||
}
|
||||
|
||||
fun getTrumpf(): SortedCardList {
|
||||
val trumpf = SortedCardList(gameType)
|
||||
|
||||
gameType.symbol?.let {
|
||||
trumpf.add(get(it))
|
||||
} ?: run {
|
||||
trumpf.add(get(CardSymbol.OBER))
|
||||
trumpf.add(get(CardSymbol.UNTER))
|
||||
}
|
||||
|
||||
gameType.color?.let {
|
||||
try{ trumpf.add(get(it)) } catch (_: CardAlreadyAddedException) {}
|
||||
}
|
||||
|
||||
return trumpf
|
||||
}
|
||||
|
||||
private fun sort() {
|
||||
super.sortInternal(gameType.symbol, gameType.color)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,4 @@
|
||||
package de.heiserer
|
||||
|
||||
interface CardList {
|
||||
fun add(card: Card)
|
||||
fun add(cards: CardList)
|
||||
fun remove(card: Card): Card
|
||||
fun remove(cards: CardList)
|
||||
fun removeLast(): Card
|
||||
fun get(index: Int): Card
|
||||
fun getCopyOfCards(): List<Card>
|
||||
fun size(): Int
|
||||
fun print()
|
||||
fun asSortedCardList(type: GameType): SortedCardList
|
||||
fun removeFirst(): Card
|
||||
}
|
||||
package de.heiserer.cards
|
||||
|
||||
open class UnsortedCardList(withAllCards: Boolean = false): CardList {
|
||||
private val cards: MutableList<Card> = if(withAllCards){
|
||||
@@ -21,6 +7,10 @@ open class UnsortedCardList(withAllCards: Boolean = false): CardList {
|
||||
mutableListOf()
|
||||
}
|
||||
|
||||
override operator fun contains(card: Card): Boolean {
|
||||
return cards.contains(card)
|
||||
}
|
||||
|
||||
override fun add(card: Card) {
|
||||
if (card !in cards) {
|
||||
cards.add(card)
|
||||
@@ -60,6 +50,10 @@ open class UnsortedCardList(withAllCards: Boolean = false): CardList {
|
||||
return cards.removeLast()
|
||||
}
|
||||
|
||||
override fun indexOf(card: Card): Int {
|
||||
return cards.indexOf(card)
|
||||
}
|
||||
|
||||
override fun get(index: Int): Card {
|
||||
if(index < 0 || index >= cards.size){
|
||||
throw IllegalArgumentException("Index $index is out of bounds.")
|
||||
@@ -67,6 +61,10 @@ open class UnsortedCardList(withAllCards: Boolean = false): CardList {
|
||||
return cards[index]
|
||||
}
|
||||
|
||||
override fun getLast(): Card {
|
||||
return cards[cards.size - 1]
|
||||
}
|
||||
|
||||
protected fun get(color: CardColor): CardList {
|
||||
val list = UnsortedCardList()
|
||||
cards.forEach { card ->
|
||||
@@ -129,52 +127,3 @@ open class UnsortedCardList(withAllCards: Boolean = false): CardList {
|
||||
}.thenComparing(compareBy({ it.color.order }, { it.symbol.order })))
|
||||
}
|
||||
}
|
||||
|
||||
class SortedCardList(private val gameType: GameType, withAllCards: Boolean = false) : UnsortedCardList(withAllCards) {
|
||||
private fun sort() {
|
||||
super.sortInternal(gameType.symbol, gameType.color)
|
||||
}
|
||||
|
||||
fun getCardsWithoutTrumpf(color: CardColor? = null): SortedCardList {
|
||||
val cardsWithoutTrumpf = SortedCardList(gameType)
|
||||
|
||||
color?.let { cardsWithoutTrumpf.add(get(it)) }?: cardsWithoutTrumpf.add(this)
|
||||
try {
|
||||
cardsWithoutTrumpf.remove(getTrumpf())
|
||||
} catch (_: IllegalArgumentException) {
|
||||
}
|
||||
|
||||
return cardsWithoutTrumpf
|
||||
}
|
||||
|
||||
fun getTrumpf():SortedCardList {
|
||||
val trumpf = SortedCardList(gameType)
|
||||
|
||||
gameType.symbol?.let {
|
||||
trumpf.add(get(it))
|
||||
} ?: run {
|
||||
trumpf.add(get(CardSymbol.OBER))
|
||||
trumpf.add(get(CardSymbol.UNTER))
|
||||
}
|
||||
|
||||
gameType.color?.let { try{
|
||||
trumpf.add(get(it))
|
||||
} catch (_: CardAlreadyAddedException) {}
|
||||
}
|
||||
|
||||
return trumpf
|
||||
}
|
||||
|
||||
fun farbFreiOrTrumpf(firstCard: Card, gameType: GameType) =
|
||||
firstCard.color == gameType.color || getCardsWithoutTrumpf(gameType.color).size() == 0
|
||||
|
||||
override fun add(card: Card) {
|
||||
super.add(card)
|
||||
sort()
|
||||
}
|
||||
}
|
||||
|
||||
class CardAlreadyAddedException(message: String, val card: Card) : RuntimeException(message) {
|
||||
|
||||
// You can add additional constructors or methods if needed, but for now, this is sufficient to handle the scenario of adding a card that's already in the list.
|
||||
}
|
||||
38
Backend/schafkopf-shared/src/main/kotlin/player/NPCPlayer.kt
Normal file
38
Backend/schafkopf-shared/src/main/kotlin/player/NPCPlayer.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package de.heiserer.player
|
||||
|
||||
import de.heiserer.cards.*
|
||||
|
||||
class NPCPlayer(name: String) : Player(name){
|
||||
override fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card {
|
||||
if(tableCards.size() == 0){
|
||||
return playTrumpf()
|
||||
}
|
||||
|
||||
val firstCard = tableCards.get(0)
|
||||
return if(CardToolkit.isTrumpf(gameType, firstCard)){
|
||||
playTrumpf()
|
||||
} else {
|
||||
playColor(firstCard.color)
|
||||
}
|
||||
}
|
||||
|
||||
private fun playTrumpf(): Card {
|
||||
val trumpfCards = cards.getTrumpf()
|
||||
if (trumpfCards.size() > 0) {
|
||||
val card = trumpfCards.removeLast()
|
||||
return cards.remove(card)
|
||||
} else {
|
||||
return cards.removeFirst()
|
||||
}
|
||||
}
|
||||
|
||||
private fun playColor(color: CardColor): Card {
|
||||
val colorCards = cards.getCardsWithoutTrumpf(color)
|
||||
if (colorCards.size() > 0) {
|
||||
val card = colorCards.removeLast()
|
||||
return cards.remove(card)
|
||||
} else {
|
||||
return cards.removeFirst()
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Backend/schafkopf-shared/src/main/kotlin/player/Player.kt
Normal file
35
Backend/schafkopf-shared/src/main/kotlin/player/Player.kt
Normal file
@@ -0,0 +1,35 @@
|
||||
package de.heiserer.player
|
||||
|
||||
import de.heiserer.cards.*
|
||||
|
||||
abstract class Player(private var name: String){
|
||||
protected var cards: SortedCardList = SortedCardList(GameType.SAU_SPIEL)
|
||||
|
||||
fun serveCards(cards: UnsortedCardList){
|
||||
this.cards = cards.asSortedCardList(GameType.SAU_SPIEL)
|
||||
}
|
||||
|
||||
fun sortCards(gameType: GameType){
|
||||
cards = cards.asSortedCardList(gameType)
|
||||
}
|
||||
|
||||
abstract fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card
|
||||
|
||||
fun getName(): String{
|
||||
return name
|
||||
}
|
||||
|
||||
protected fun validateCard(card: Card, tableCards: UnsortedCardList, gameType: GameType): Boolean{
|
||||
if(tableCards.size() == 0){
|
||||
return true
|
||||
}
|
||||
|
||||
val firstCard = tableCards.get(0)
|
||||
|
||||
return if(CardToolkit.isTrumpf(gameType, firstCard)){
|
||||
CardToolkit.isTrumpf(gameType, card) || cards.getTrumpf().size() == 0
|
||||
} else {
|
||||
card.color == firstCard.color || cards.getCardsWithoutTrumpf(firstCard.color).size() == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import de.heiserer.*
|
||||
import de.heiserer.cards.Card
|
||||
import de.heiserer.cards.CardColor
|
||||
import de.heiserer.cards.GameType
|
||||
import de.heiserer.cards.UnsortedCardList
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
|
||||
25
Backend/schafkopf-shared/src/test/kotlin/CardToolkitTest.kt
Normal file
25
Backend/schafkopf-shared/src/test/kotlin/CardToolkitTest.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
import de.heiserer.cards.*
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CardToolkitTest {
|
||||
@Test
|
||||
fun `test isTrumpf`() {
|
||||
// ASSERT AND ACT
|
||||
assertEquals(true, CardToolkit.isTrumpf(GameType.SAU_SPIEL, Card.HERZ_O))
|
||||
assertEquals(false, CardToolkit.isTrumpf(GameType.SAU_SPIEL, Card.SCHELL_7))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whoTricks() {
|
||||
// ARRANGE
|
||||
val cards = UnsortedCardList()
|
||||
cards.add(Card.SCHELL_7)
|
||||
cards.add(Card.SCHELL_8)
|
||||
cards.add(Card.SCHELL_K)
|
||||
cards.add(Card.SCHELL_O)
|
||||
|
||||
// ASSERT AND ACT
|
||||
assertEquals(3, CardToolkit.whoTricks(GameType.SAU_SPIEL, cards))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user