Skip to content

Instantly share code, notes, and snippets.

@lynnfield
Last active August 4, 2022 09:05
Show Gist options
  • Save lynnfield/2e7f44c01a924f632a585bfa84840a4a to your computer and use it in GitHub Desktop.
Save lynnfield/2e7f44c01a924f632a585bfa84840a4a to your computer and use it in GitHub Desktop.
import Piece.Companion.fromNotation
// Chess game
// Load state of Game
val notation = listOf("pa3", "qc5")
fun main() {
val toPieceTypeContext = object : ToPieceTypeContext {
override val charToPieceType = super.charToPieceType
}
val toChessColumnContext = object : ToChessColumnContext {
override val charToChessColumn = super.charToChessColumn
}
val toChessRowContext = object : ToChessRowContext {
override val charToChessRow = super.charToChessRow
}
val fromNotationContext = object : FromNotationContext {
override fun fromTriple(string: String) = toTriple(string)
override fun convertToPieceType(char: Char) = toPieceTypeContext.toPieceType(char)
override fun convertToColumn(char: Char) = toChessColumnContext.toChessColumn(char)
override fun convertToRow(char: Char) = toChessRowContext.toChessRow(char)
override fun toPiece(pieceType: PieceType, chessColumn: ChessColumn, chessRow: ChessRow) =
asPiece(pieceType, chessColumn, chessRow)
}
val gameState = notation.map { with(fromNotationContext) { fromNotation(it) } }
}
sealed class Piece(val column: ChessColumn, val row: ChessRow) {
companion object {
fun FromNotationContext.fromNotation(piece: String): Result<Piece> = runCatching {
val (pieceType, column, row) = fromTriple(piece).getOrThrow()
val pieceTypeValue = convertToPieceType(pieceType).getOrThrow()
val chessColumn = convertToColumn(column).getOrThrow()
val chessRow = convertToRow(row).getOrThrow()
toPiece(pieceTypeValue, chessColumn, chessRow)
}.recoverCatching { throw Exception("'$piece' is not a Piece", it) }
}
}
interface FromNotationContext {
fun fromTriple(string: String): Result<Triple<Char, Char, Char>>
fun convertToPieceType(char: Char): Result<PieceType>
fun convertToColumn(char: Char): Result<ChessColumn>
fun convertToRow(char: Char): Result<ChessRow>
fun toPiece(pieceType: PieceType, chessColumn: ChessColumn, chessRow: ChessRow): Piece
}
fun asPiece(pieceType: PieceType, chessColumn: ChessColumn, chessRow: ChessRow): Piece {
return when (pieceType) {
PieceType.Pawn -> Pawn(chessColumn, chessRow)
PieceType.Queen -> Queen(chessColumn, chessRow)
}
}
fun toTriple(value: String): Result<Triple<Char, Char, Char>> = runCatching {
check(value.length == 3) { "value should have 3 characters '$value'" }
Triple(value[0], value[1], value[2])
}
class Pawn(column: ChessColumn, row: ChessRow) : Piece(column, row)
class Queen(column: ChessColumn, row: ChessRow) : Piece(column, row)
enum class PieceType { Pawn, Queen; }
fun toChar(pieceType: PieceType): Char {
return when (pieceType) {
PieceType.Pawn -> 'p'
PieceType.Queen -> 'q'
}
}
interface ToPieceTypeContext {
val charToPieceType: Map<Char, PieceType> get() = PieceType.values().associateBy { toChar(it) }
}
fun ToPieceTypeContext.toPieceType(char: Char): Result<PieceType> = runCatching {
charToPieceType[char.lowercaseChar()] ?: error("'$char' is not a PieceType, possible values are ${charToPieceType.keys}")
}
enum class ChessColumn { A, C; }
fun toChar(chessColumn: ChessColumn): Char {
return when (chessColumn) {
ChessColumn.A -> 'a'
ChessColumn.C -> 'c'
}
}
interface ToChessColumnContext {
val charToChessColumn: Map<Char, ChessColumn> get() = ChessColumn.values().associateBy { toChar(it) }
}
fun ToChessColumnContext.toChessColumn(char: Char): Result<ChessColumn> = runCatching {
charToChessColumn[char.lowercaseChar()]
?: error("'$char' is not a ChessColumn, possible values are ${charToChessColumn.keys}")
}
enum class ChessRow { Third, Fifth; }
fun toChar(chessRow: ChessRow): Char {
return when (chessRow) {
ChessRow.Third -> '3'
ChessRow.Fifth -> '5'
}
}
interface ToChessRowContext {
val charToChessRow: Map<Char, ChessRow> get() = ChessRow.values().associateBy { toChar(it) }
}
fun ToChessRowContext.toChessRow(char: Char): Result<ChessRow> = runCatching {
charToChessRow[char] ?: error("'$char' is not a ChessRow, possible values are ${charToChessRow.keys}")
}
import Piece.Companion.fromNotation
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertSame
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.lang.Exception
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
import kotlin.test.assertTrue
class Proofs {
@Test
fun `toChar is returning unique value for each ChessColumn`() {
val charsToChessColumns = ChessColumn.values().groupBy { toChar(it) }
assertAll(
charsToChessColumns.map { (key, values) ->
{
assertTrue(values.size == 1, "$values are assigned for the same key $key")
}
}
)
}
@Test
fun `ChessColumn toChar not changed since last version`() {
val expected = "[a, c]"
val actual = ChessColumn.values().map { toChar(it) }.toString()
assertEquals(expected, actual)
}
@Test
fun `toChessColumn is taking value from map`() {
val char = 'a'
val chessColumn = ChessColumn.C
val context = object : ToChessColumnContext {
override val charToChessColumn = mapOf(char to chessColumn)
}
val result = with(context) { toChessColumn(char) }
assertEquals(chessColumn, result.getOrThrow())
}
@Test
fun `toChessColumn is not case sensitive`() {
val char = 'a'
val chessColumn = ChessColumn.C
val context = object : ToChessColumnContext {
override val charToChessColumn = mapOf(char to chessColumn)
}
val result = with(context) { toChessColumn(char.uppercaseChar()) }
assertEquals(chessColumn, result.getOrThrow())
}
@Test
fun `toChessColumn is failing when value is not found in the map`() {
val char = 'a'
val chessColumn = ChessColumn.C
val context = object : ToChessColumnContext {
override val charToChessColumn = mapOf(char to chessColumn)
}
val result = with(context) { toChessColumn('b') }
assertEquals(
"'b' is not a ChessColumn, possible values are ${context.charToChessColumn.keys}",
result.exceptionOrNull()?.message
)
}
@Test
fun `toChar is returning unique value for each ChessRow`() {
val charsToChessRows = ChessRow.values().groupBy { toChar(it) }
assertAll(
charsToChessRows.map { (key, values) ->
{
assertTrue(values.size == 1, "$values are assigned for the same key $key")
}
}
)
}
@Test
fun `ChessRow toChar not changed since last version`() {
val expected = "[3, 5]"
val actual = ChessRow.values().map { toChar(it) }.toString()
assertEquals(expected, actual)
}
@Test
fun `toChessRow is taking value from map`() {
val char = 'a'
val chessRow = ChessRow.Third
val context = object : ToChessRowContext {
override val charToChessRow = mapOf(char to chessRow)
}
val result = with(context) { toChessRow(char) }
assertEquals(chessRow, result.getOrThrow())
}
@Test
fun `toChessRow is failing when value is not found in the map`() {
val char = 'a'
val chessRow = ChessRow.Third
val context = object : ToChessRowContext {
override val charToChessRow = mapOf(char to chessRow)
}
val result = with(context) { toChessRow('b') }
assertEquals(
"'b' is not a ChessRow, possible values are ${context.charToChessRow.keys}",
result.exceptionOrNull()?.message
)
}
@Test
fun `toChar is returning unique value for each PieceType`() {
val charsToPieceTypes = PieceType.values().groupBy { toChar(it) }
assertAll(
charsToPieceTypes.map { (key, values) ->
{
assertTrue(values.size == 1, "$values are assigned for the same key $key")
}
}
)
}
@Test
fun `PieceType toChar not changed since last version`() {
val expected = "[p, q]"
val actual = PieceType.values().map { toChar(it) }.toString()
assertEquals(expected, actual)
}
@Test
fun `toPieceType is taking value from map`() {
val char = 'a'
val PieceType = PieceType.Pawn
val context = object : ToPieceTypeContext {
override val charToPieceType = mapOf(char to PieceType)
}
val result = with(context) { toPieceType(char) }
assertEquals(PieceType, result.getOrThrow())
}
@Test
fun `toPieceType is not case sensitive`() {
val char = 'a'
val PieceType = PieceType.Pawn
val context = object : ToPieceTypeContext {
override val charToPieceType = mapOf(char to PieceType)
}
val result = with(context) { toPieceType(char.uppercaseChar()) }
assertEquals(PieceType, result.getOrThrow())
}
@Test
fun `toPieceType is failing when value is not found in the map`() {
val char = 'a'
val PieceType = PieceType.Pawn
val context = object : ToPieceTypeContext {
override val charToPieceType = mapOf(char to PieceType)
}
val result = with(context) { toPieceType('b') }
assertEquals(
"'b' is not a PieceType, possible values are ${context.charToPieceType.keys}",
result.exceptionOrNull()?.message
)
}
@Test
fun `fromNotation is repackaging exception`() {
val exception = Exception()
val context = object : FromNotationContext {
override fun fromTriple(string: String): Result<Triple<Char, Char, Char>> = failure(exception)
override fun convertToPieceType(char: Char) = success(PieceType.Pawn)
override fun convertToColumn(char: Char) = success(ChessColumn.C)
override fun convertToRow(char: Char) = success(ChessRow.Third)
override fun toPiece(pieceType: PieceType, chessColumn: ChessColumn, chessRow: ChessRow) =
Pawn(ChessColumn.C, ChessRow.Third)
}
val input = "asd"
val errorMessage = "'$input' is not a PieceType"
val result = with(context) { fromNotation(input) }
assertSame(exception, result.exceptionOrNull()?.cause)
assertEquals(errorMessage, result.exceptionOrNull()?.message)
}
data class FromNotationProofCase(
val fromTriple: Result<Triple<Char, Char, Char>>,
val convertToPieceType: Result<PieceType>,
val convertToColumn: Result<ChessColumn>,
val convertToRow: Result<ChessRow>,
val toPiece: Piece,
val expected: Result<Piece>,
)
@ParameterizedTest
@MethodSource("cases for fromNotation proofs")
fun `fromNotation proofs`(case: FromNotationProofCase) = with (case) {
val context = object : FromNotationContext {
override fun fromTriple(string: String) = fromTriple
override fun convertToPieceType(char: Char) = convertToPieceType
override fun convertToColumn(char: Char) = convertToColumn
override fun convertToRow(char: Char) = convertToRow
override fun toPiece(pieceType: PieceType, chessColumn: ChessColumn, chessRow: ChessRow) = toPiece
}
val result = with(context) { fromNotation("") }
assertSame(expected.exceptionOrNull(), result.exceptionOrNull()?.cause)
assertEquals(expected.getOrNull(), result.getOrNull())
}
companion object {
@JvmStatic
fun `cases for fromNotation proofs`(): List<FromNotationProofCase> {
val piece = Queen(ChessColumn.C, ChessRow.Third)
val success = FromNotationProofCase(
fromTriple = success(Triple('a', 'a', 'a')),
convertToPieceType = success(PieceType.Pawn),
convertToColumn = success(piece.column),
convertToRow = success(piece.row),
toPiece = piece,
expected = success(piece),
)
val exception = Exception()
return listOf(
success,
success.copy(fromTriple = failure(exception), expected = failure(exception)),
success.copy(convertToPieceType = failure(exception), expected = failure(exception)),
success.copy(convertToColumn = failure(exception), expected = failure(exception)),
success.copy(convertToRow = failure(exception), expected = failure(exception)),
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment