Created
August 12, 2019 22:48
-
-
Save BrandonDyer64/5f3754a86c9e40eda2c4abaa48454153 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
/* | |
Tic-Tac-Toe | |
━━━━━━━━━━━ | |
Kevin Language Example program | |
By: Brandon Dyer <github.com/BrandonDyer64> | |
*/ | |
import Console from "console" | |
import Math from "math" | |
import { CheckWin, CheckDraw } from "./CheckBoard" | |
// Constant 8-bit integers | |
// These are automatically cast by the compiler | |
let i8 width = 3; | |
let i8 height = 3; | |
// The Main function is where our program starts | |
fn Main([String] args) { | |
// Multiline strings start and end with """ | |
// Indentation is handled automatically | |
Console::Print(""" | |
Tic-Tac-Toe | |
━━━━━━━━━━━ | |
"""); | |
// This loop will keep calling PlayGame | |
// until PlayGame returns true | |
loop => PlayGame(); | |
Console::Print(""" | |
━━━━━━━━━━━ | |
Thanks for playing! | |
"""); | |
} | |
fn bool PlayGame() { | |
// Our board is an array of 2-bit integers (values 0-3) | |
// We use the `build` keyword to fill the array with zeros | |
let mut board = build (0, width * height, i) => 0i2; | |
// Keep looping until PlayRound returns a winner | |
// board.PlayRound() is the same as PlayRound(board) | |
let winner = loop => board.PlayRound(); | |
// Output who the winner is | |
let result = switch winner { | |
:player => "You win!"; | |
:ai => "You lose."; | |
default => "Draw."; | |
}; | |
result.Console::Print(); | |
// We can ask questions in the terminal with Console::Ask | |
return Console::Ask("Would you like to play again? [y/N]: ").LowerCase() != "y" | |
} | |
fn symbol PlayRound(mut [i2] board) { | |
// Function pointers allow us to use inversion of control | |
if board.Move(2, &MoveAI) return :ai; | |
if board.Move(1, &MovePlayer) return :player; | |
// Check for draw | |
if board.CheckDraw() return :draw; | |
// The `:none` symbol is treated as false by if statements | |
return :none; | |
} | |
fn bool Move(mut [i2] board, i2 num, fn i8([i2]) fun) { | |
// We call our fun variable as a lambda | |
// With lambdas, we can't use `board.fun()` as fun may | |
// be a field | |
// We could instead use `board |> fun()` | |
let move = fun(board); | |
// Modify the board value | |
// This is why the paramater type is `mut [i2]` | |
// `mut` lets us modify the contents of the array | |
board[move] = num; | |
return board.CheckWin(); | |
} | |
fn i8 MoveAI([i2] board) { | |
// The standard library gives us some neat functions | |
// such as `Math::Random` | |
let i8 move = Math::RandomInt(1, 10); | |
if !board.CheckMove(move) return MoveAI(board); | |
return move; | |
} | |
fn i8 MovePlayer([i2] board) { | |
// We can insert values into strings | |
// like this `${value}` | |
Console::Print(""" | |
━━━━━━━━━━━ | |
${board.PrettyState()} | |
\0[BLU] | |
1 ┃ 2 ┃ 3 | |
━━━╋━━━╋━━━ | |
4 ┃ 5 ┃ 6 | |
━━━╋━━━╋━━━ | |
7 ┃ 8 ┃ 9 | |
\0[DEF] | |
"""); | |
let i8 move = Console::AskInteger("Your move [1-${width * height}]: ") - 1; | |
// Recursion is only safe when it is the last statement | |
// It is allowed here because it is the only call in the | |
// return expression | |
// If we had other calls, we'd need to tell the compiler | |
// to allow unsafe recursion with `#[allow(recursion)]` | |
if !board.CheckMove(move) { | |
Console::Print("Invalid move."); | |
return MovePlayer(board); | |
} | |
return move; | |
} | |
// Functions that immediately return don't need curly braces | |
// We can just use `=>` followed by an expression | |
fn bool CheckMove([i2] board, i8 move) => | |
// This expression starts with an `and` operator, so | |
// `and` can be the only operator chained to this expression | |
// We could do this with either `and` or `&&` | |
and 1 <= move <= width * height | |
and board[move] == 0; | |
fn String PrettyState([i2] board) => | |
board map (el, i) => | |
// Likewise, this expression starts with a `+` operator | |
+ switch el { | |
1 => " \0[GRN]o\0[DEF] "; | |
2 => " \0[RED]x\0[DEF] "; | |
default => " "; | |
}; | |
+ i % width != 0 | |
? "\0[BLU]┃\0[DEF]" | |
: "\n" | |
+ i % width == 0 and i < width * height - 1 | |
? "\0[BLU]━━━╋━━━╋━━━\0[DEF]\n" | |
: ""; | |
// We export whatever function we want to use as a starting | |
// point for our program | |
export Main; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment