Untitled
unknown
plain_text
12 days ago
6.9 kB
4
Indexable
import gleam/list import gleam/option.{None, Some} import lustre import lustre/attribute import lustre/element.{type Element} import lustre/element/html import lustre/event // === GAME TYPES === pub type Disc { Red Yellow Empty } pub type Column = List(Disc) pub type Board = List(Column) pub type Player { PlayerRed PlayerYellow } pub type Model { Model(board: Board, current_player: Player, winner: option.Option(Player)) } pub type Msg { ClickColumn(Int) Restart } // === INIT === pub fn construct_board() -> Board { list.repeat(Empty, times: 6) |> fn(column) { list.repeat(column, times: 7) } } fn init(_flags) -> Model { let board = construct_board() Model(board: board, current_player: PlayerRed, winner: None) } // === UPDATE === fn update(model: Model, msg: Msg) -> Model { case msg { Restart -> init(Nil) ClickColumn(col_index) -> case model.winner { Some(_) -> model None -> handle_turn(model, col_index) } } } fn handle_turn(model: Model, col_index: Int) -> Model { let disc = case model.current_player { PlayerRed -> Red PlayerYellow -> Yellow } let updated_board = update_board(model.board, col_index, disc) let has_won = check_for_win(updated_board, disc) let next_player = case has_won { True -> model.current_player False -> toggle_player(model.current_player) } let winner = case has_won { True -> Some(model.current_player) False -> None } Model(board: updated_board, current_player: next_player, winner: winner) } fn toggle_player(player: Player) -> Player { case player { PlayerRed -> PlayerYellow PlayerYellow -> PlayerRed } } // === VIEW HELPERS === fn render_disc(disc: Disc) -> Element(Msg) { let class = case disc { Red -> "disc red" Yellow -> "disc yellow" Empty -> "disc empty" } html.div([attribute.class(class)], []) } fn render_column(index: Int, column: Column) -> Element(Msg) { let disc_elements = list.reverse(column) |> list.map(render_disc) html.div( [attribute.class("column"), event.on_click(ClickColumn(index))], disc_elements, ) } // === VIEW === fn view(model: Model) -> Element(Msg) { let column_elements = list.index_map(model.board, fn(column, index) { render_column(index, column) }) let status_text = case model.winner { Some(PlayerRed) -> "Red wins!" Some(PlayerYellow) -> "Yellow wins!" None -> case model.current_player { PlayerRed -> "Red's turn" PlayerYellow -> "Yellow's turn" } } html.div([attribute.class("game-container")], [ html.h1([attribute.class("banner")], [html.text("Vier op een rij!")]), // 👈 ADD THIS html.h2([], [html.text(status_text)]), html.div([attribute.class("board")], column_elements), html.button([event.on_click(Restart)], [html.text("Restart")]), ]) } // === MAIN === pub fn main() { let app = lustre.simple(init, update, view) let assert Ok(_) = lustre.start(app, "#app", Nil) Nil } // === GAME LOGIC === pub fn place_disc(column: Column, disc: Disc) -> Column { case column { [_, _, _, _, _, Empty] -> [Empty, Empty, Empty, Empty, Empty, disc] [_, _, _, _, Empty, r5] -> [Empty, Empty, Empty, Empty, disc, r5] [_, _, _, Empty, r4, r5] -> [Empty, Empty, Empty, disc, r4, r5] [_, _, Empty, r3, r4, r5] -> [Empty, Empty, disc, r3, r4, r5] [_, Empty, r2, r3, r4, r5] -> [Empty, disc, r2, r3, r4, r5] [Empty, r1, r2, r3, r4, r5] -> [disc, r1, r2, r3, r4, r5] _ -> column } } pub fn update_board(board: Board, column_number: Int, disc: Disc) -> Board { case board { [c0, ..tail] if column_number == 0 -> [place_disc(c0, disc), ..tail] [c0, c1, ..tail] if column_number == 1 -> [c0, place_disc(c1, disc), ..tail] [c0, c1, c2, ..tail] if column_number == 2 -> [ c0, c1, place_disc(c2, disc), ..tail ] [c0, c1, c2, c3, ..tail] if column_number == 3 -> [ c0, c1, c2, place_disc(c3, disc), ..tail ] [c0, c1, c2, c3, c4, ..tail] if column_number == 4 -> [ c0, c1, c2, c3, place_disc(c4, disc), ..tail ] [c0, c1, c2, c3, c4, c5, ..tail] if column_number == 5 -> [ c0, c1, c2, c3, c4, place_disc(c5, disc), ..tail ] [c0, c1, c2, c3, c4, c5, c6] if column_number == 6 -> [ c0, c1, c2, c3, c4, c5, place_disc(c6, disc), ] _ -> board } } pub fn get_disc(board: Board, column_number: Int, row_number: Int) -> Disc { case list.drop(board, column_number) { [column, ..] -> case list.drop(column, row_number) { [disc, ..] -> disc [] -> Empty } [] -> Empty } } pub fn check_for_win(board: Board, disc: Disc) -> Bool { check_for_win_vertical(board, disc) || check_for_win_horizontal(board, disc) || check_for_win_diagonal_up(board, disc) || check_for_win_diagonal_down(board, disc) } pub fn check_for_win_vertical(board: Board, disc: Disc) -> Bool { let col_range = list.range(0, 6) let row_range = list.range(0, 2) list.any(col_range, fn(col) { list.any(row_range, fn(row) { get_disc(board, col, row) == disc && get_disc(board, col, row + 1) == disc && get_disc(board, col, row + 2) == disc && get_disc(board, col, row + 3) == disc }) }) } pub fn check_for_win_horizontal(board: Board, disc: Disc) -> Bool { let col_range = list.range(0, 3) let row_range = list.range(0, 5) list.any(col_range, fn(col) { list.any(row_range, fn(row) { get_disc(board, col, row) == disc && get_disc(board, col + 1, row) == disc && get_disc(board, col + 2, row) == disc && get_disc(board, col + 3, row) == disc }) }) } pub fn check_for_win_diagonal_down(board: Board, disc: Disc) -> Bool { let col_range = list.range(0, 3) let row_range = list.range(0, 2) list.any(col_range, fn(col) { list.any(row_range, fn(row) { get_disc(board, col, row) == disc && get_disc(board, col + 1, row + 1) == disc && get_disc(board, col + 2, row + 2) == disc && get_disc(board, col + 3, row + 3) == disc }) }) } pub fn check_for_win_diagonal_up(board: Board, disc: Disc) -> Bool { let col_range = list.range(0, 3) let row_range = list.range(3, 5) list.any(col_range, fn(col) { list.any(row_range, fn(row) { get_disc(board, col, row) == disc && get_disc(board, col + 1, row - 1) == disc && get_disc(board, col + 2, row - 2) == disc && get_disc(board, col + 3, row - 3) == disc }) }) }
Editor is loading...
Leave a Comment