Last active
August 4, 2022 09:05
-
-
Save lynnfield/2e7f44c01a924f632a585bfa84840a4a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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