// ---- BasicRoles.kt ---- \\

class Civilian: Role() {
    override val alignment = Alignment.GOOD
    override fun canAct(daypart: Game.Daypart, date: Int) = false
}

class Werewolf: Role() {
    override val alignment = Alignment.EVIL
    override fun canAct(daypart: Game.Daypart, date: Int) = daypart == Game.Daypart.NIGHT
}


// ---- PlayerProperties.kt ---- \\

/**
 * A PlayerProperty can be added to a Player's properties field to indicate a state.
 */
abstract class PlayerProperty

/**
 * Number of (successful) actions the Player has performed.
 */
data class ActionCount(var count: Int): PlayerProperty()

/**
 * Indicates that the Player can no longer perform their action.
 */
object ActionLimitReached: PlayerProperty()


// ---- RoleSets.kt ---- \\

/**
 * Generates a game with only normal Civilians and normal Werewolves.
 * @param amount Amount of players.
 * @param wolves Determines how many of the players are werewolves.
 */
fun civiliansWolves(amount: Int, wolves: (Int) -> Int = { it.floorDiv(4) }) = buildList {
    repeat(wolves(amount)) { add(Werewolf()) }
    repeat(amount - wolves(amount)) { add(Civilian()) }
}.shuffled()


// ---- Game.kt ---- \\

/**
 * A Player of Weerwolven.
 * @param name Name of the Player.
 * @param role Role of the Player, NullRole by default.
 */
data class Player(val name: String, var role: Role = NullRole) {
    /**
     * Whether this Player is alive.
     */
    private var isAlive = true

    /**
     * Whether this Player can vote.
     */
    private var canVote = true

    /**
     * Keeps the state of a Player.
     */
    val properties = mutableListOf<PlayerProperty>()

    /**
     * Returns true if the Player is alive.
     */
    fun isAlive() = isAlive

    /**
     * Sets isAlive to false because the Player dies.
     */
    fun dies() {
        isAlive = false
    }

    fun canAct(daypart: Game.Daypart, date: Int) = role.canAct(daypart, date) && ActionLimitReached !in properties
}

/**
 * Alignment that a Player can have.
 * @instance GOOD Wins with civilians.
 * @instance NEUTRAL Has own win condition, does not prevent anyone from winning.
 * @instance EVIL Wins with wolves.
 * @instance ALONE Has own win condition, prevents everyone from winning.
 */
enum class Alignment {
    GOOD,    // Good roles (winning with civilians)
    NEUTRAL, // Neutral roles (do not prevent anyone from winning)
    EVIL,    // Evil Roles (winning with wolves)
    ALONE;   // Roles that only win by themselves (prevent others from winning)

    fun isGood() = this == GOOD || this == NEUTRAL
    fun isEvil() = this == EVIL || this == NEUTRAL
}

/**
 * Role that a Player can have.
 * @param actionPriority Determines the order in which this role acts at night, null if the Role does not act at night.
 */
abstract class Role(val actionPriority: Int? = null) {
    /**
     * Alignment of the Role (Civilians, Werewolves, Neutral, Alone).
     */
    abstract val alignment: Alignment

    /**
     * Returns true if the Player can perform their action at the current time in their current state.
     */
    abstract fun canAct(daypart: Game.Daypart, date: Int): Boolean

    /**
     * Can be called when the Player is successfully eaten by the werewolves.
     */
    fun whenEaten() {}

    /**
     * Can be called when the Player is lynched by the town.
     */
    fun whenLynched() {}
}

/**
 * Default Role of every Player, will throw a NotImplementedError on any method.
 */
object NullRole: Role() {
    override val alignment = throw NotImplementedError("Null Role")
    override fun canAct(daypart: Game.Daypart, date: Int) = throw NotImplementedError("Null Role")
    override fun toString() = "Null Role"
}

class Game(val players: List<Player>, val mode: GameMode = GameMode.SEQUENTIAL) {
    /**
     * Ways that the Game can be played.
     * @instance SEQUENTIAL All actions are called one-by-one and fed back, anytime-roles are not possible.
     * @instance CONCURRENT Actions are picked over time, feedback comes at once, anytime-roles are possible.
     */
    enum class GameMode {
        SEQUENTIAL, // All actions are called one-by-one and fed back, anytime-roles are not possible.
        CONCURRENT, // Actions are picked over time, feedback comes at once, anytime-roles are possible.
    }

    /**
     * Mayor of Wakkerdam, is elected on Day 0.
     */
    var mayor: Player = throw NullPointerException()

    enum class Daypart {DAY, NIGHT}


    /**
     * Time of the game, split in Daypart (Day or Night) and Date (number of the Day/Night).
     */
    private var time = Daypart.DAY to 0

    /**
     * Returns the in-game time.
     */
    fun time() = time

    /**
     * Advances the time from the current Day to the next Night or from the current Night to the next Day.
     */
    fun advanceTime() {
        time = time.let { (daypart, date) ->
            if (daypart == Daypart.DAY) Daypart.NIGHT to date + 1 else Daypart.DAY to date
        }
    }

    /**
     * Returns true if the Game is over, i.e. some party has won.
     */
    fun isOver() = listOf(
        winCivilians(),
        winWolves(),
    ).any()

    // --- --- Begin Win Conditions --- --- \\

    fun winCivilians() = alivePlayers().all { it.role.alignment.isGood() }

    fun winWolves() = alivePlayers().all { it.role.alignment.isEvil() }

    // --- ---  End Win Conditions  --- --- \\

    /**
     * Returns all Players that are alive.
     */
    fun alivePlayers() = players.filter { it.isAlive() }

    /**
     * Returns all Players that have died.
     */
    fun deadPlayers() = players.filterNot { it.isAlive() }

    companion object {
        /**
         * Runs the Game.
         */
        fun main() {
            println("Who live in Wakkerdam?")
            // Create players with only names and generate their role later
            // FUTURE: add option to also specify role here
            val players = buildList {
                var line = readln()
                while (line != "") {
                    add(Player(line))
                    line = readln()
                }
            }

            civiliansWolves(players.size).let { roles -> players.forEachIndexed { i, p -> p.role = roles[i] } }

            val game = Game(players)

            game.players.forEach { println(it.toString()) }
        }
    }
}

fun main() { Game.main() } 

Kotlin online compiler

Write, Run & Share Kotlin code online using OneCompiler’s Kotlin online compiler for free. It’s a modern and fast online playground for Kotlin, supporting the latest version and ideal for learning, experimenting, and sharing code instantly.

About Kotlin

Kotlin is a statically typed, modern programming language developed by JetBrains. It runs on the JVM and is fully interoperable with Java. Kotlin is concise, expressive, and safe, and it’s officially supported by Google for Android app development.

Sample Code

The following is a simple Kotlin program that prints a greeting:

fun main() {
    println("Hello, OneCompiler!")
}

Taking inputs (stdin)

OneCompiler’s Kotlin editor supports stdin. You can provide input using the I/O tab. Here's a sample program that reads a line of input and prints a greeting:

fun main() {
    print("Enter your name: ")
    val name = readLine()
    println("Hello, $name")
}

Syntax Basics

Variables

val name: String = "OneCompiler"  // Immutable
var age: Int = 25                 // Mutable

Kotlin supports type inference, so explicit types are optional:

val city = "Hyderabad"
var count = 10

Conditionals

val score = 85
if (score >= 50) {
    println("Pass")
} else {
    println("Fail")
}

Loops

For loop

for (i in 1..5) {
    println(i)
}

While loop

var i = 1
while (i <= 5) {
    println(i)
    i++
}

Do-While loop

var j = 1
do {
    println(j)
    j++
} while (j <= 5)

Functions

fun add(a: Int, b: Int): Int {
    return a + b
}

fun greet(name: String) = "Hello, $name"

Collections

val items = listOf("apple", "banana", "cherry")
for (item in items) {
    println(item)
}

This guide provides a quick reference to Kotlin programming syntax and features. Start coding in Kotlin using OneCompiler’s Kotlin online compiler today!