package tiny_appkits
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Application engines from scratch: a spreadsheet, rich text, paint, draw, CAD, editors and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_appkits.appkit_tui/Tui_snake.ml.html
Source file Tui_snake.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Tui_snake.mli *) (*****************************************************************************) (* The model *) (*****************************************************************************) (* the board, inside its frame *) let height = 20 let width = 40 (* seconds between two moves *) let period = 0.1 type model = { body : (int * int) list; (* head first *) dir : int * int; (* rows, columns a move *) turn : int * int; (* the direction asked for, taken at the next move *) food : int * int; seed : Lehmer.t; clock : float; (* seconds since the last move *) score : int; crashed : bool; quit : bool; } (* a cell the snake isn't on, from the generator *) let rec place (seed : Lehmer.t) (body : (int * int) list) : (int * int) * Lehmer.t = let seed = Lehmer.next seed in let i = int_of_float (Lehmer.to_unit seed *. float_of_int (height * width)) in let cell = (i / width, i mod width) in if List.mem cell body then place seed body else (cell, seed) let start (seed : Lehmer.t) : model = let body = [ (10, 12); (10, 11); (10, 10) ] in let food, seed = place seed body in { body; dir = (0, 1); turn = (0, 1); food; seed; clock = 0.; score = 0; crashed = false; quit = false } let init = start (Lehmer.of_int 42) (*****************************************************************************) (* Update *) (*****************************************************************************) (* a move: the head a cell further; the tail follows unless it ate *) let move (m : model) : model = (* no turning back into oneself *) let dir = if fst m.turn = -fst m.dir && snd m.turn = -snd m.dir then m.dir else m.turn in let r, c = List.hd m.body in let head = (r + fst dir, c + snd dir) in let ate = head = m.food in let body = head :: (if ate then m.body else List.filteri (fun i _ -> i < List.length m.body - 1) m.body) in let off = fst head < 0 || fst head >= height || snd head < 0 || snd head >= width in if off || List.mem head (List.tl body) then { m with crashed = true } else if ate then let food, seed = place m.seed body in { m with body; dir; food; seed; score = m.score + 1 } else { m with body; dir } let update (e : Tui.event) (m : model) : model = match e with | Key "q" -> { m with quit = true } | Key "r" when m.crashed -> { (start m.seed) with score = 0 } | Key ("\x1b[A" | "k") -> { m with turn = (-1, 0) } | Key ("\x1b[B" | "j") -> { m with turn = (1, 0) } | Key ("\x1b[C" | "l") -> { m with turn = (0, 1) } | Key ("\x1b[D" | "h") -> { m with turn = (0, -1) } | Key _ -> m | Tick _ when m.crashed -> m | Tick dt -> (* as many moves as periods passed, a slow frame catching up; with a hair of tolerance, since 0.3 - 0.1 - 0.1 is 0.0999...98 in floating point, and three periods must be three moves *) let rec steps (m : model) = if m.clock >= period -. 1e-9 && not m.crashed then steps (move { m with clock = m.clock -. period }) else m in steps { m with clock = m.clock +. dt } (*****************************************************************************) (* View *) (*****************************************************************************) let green = { Vt.plain with fg = Vt.Green; bold = true } let red = { Vt.plain with fg = Vt.Red; bold = true } let view (m : model) : Curses.t = let cell (r, c) = (r + 2, c + 1) in let screen = Curses.create ~rows:24 ~cols:80 |> Curses.put 0 0 "SNAKE" |> Curses.box 1 0 (height + 2) (width + 2) |> Curses.put 3 46 "arrows, or h j k l: turn" |> Curses.put 4 46 "q: quit" |> Curses.put 6 46 (Printf.sprintf "score: %d" m.score) in let fr, fc = cell m.food in let screen = Curses.put ~attrs:red fr fc "*" screen in let screen = List.fold_left (fun s (i, rc) -> let r, c = cell rc in Curses.put ~attrs:green r c (if i = 0 then "@" else "o") s) screen (List.mapi (fun i rc -> (i, rc)) m.body) in if m.crashed then Curses.put ~attrs:{ Vt.plain with reverse = true } 8 46 " CRASHED -- r: again " screen else screen let program : model Tui.program = { init; update; view; over = (fun m -> m.quit) } let body (m : model) = m.body let crashed (m : model) = m.crashed let score (m : model) = m.score
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>