added cmd playing mode

This commit is contained in:
2024-07-22 00:48:10 +02:00
parent 4a167bf3b4
commit 05300c1153
15 changed files with 434 additions and 137 deletions

View File

@@ -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()
}
}
}

View File

@@ -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>)
}

View File

@@ -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.
}

View 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
}

View 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)
}
}
}

View File

@@ -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),

View File

@@ -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)
}
}

View File

@@ -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.
}

View 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()
}
}
}

View 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
}
}
}

View File

@@ -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

View 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))
}
}