From e9b36dc9e14f6aab06423f9bf29ecb22b0ef2510 Mon Sep 17 00:00:00 2001 From: Travis Shears Date: Fri, 3 Apr 2026 18:35:13 +0200 Subject: [PATCH] render colors Press any key to quit" 10 10)) Press any key to quit" 10 10)) --- .DS_Store | Bin 0 -> 10244 bytes CLAUDE.md | 169 ++++++++++++++++++ two_player_cleaning_game/.DS_Store | Bin 0 -> 6148 bytes .../assets/level_001.aseprite | Bin 0 -> 586 bytes two_player_cleaning_game/assets/level_001.png | Bin 0 -> 434 bytes .../assets/level_002.aseprite | Bin 0 -> 372 bytes two_player_cleaning_game/assets/level_002.png | Bin 0 -> 220 bytes .../assets/player_001.aseprite | Bin 0 -> 493 bytes .../assets/player_001.png | Bin 0 -> 184 bytes two_player_cleaning_game/main.fnl | 36 +++- 10 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 .DS_Store create mode 100644 CLAUDE.md create mode 100644 two_player_cleaning_game/.DS_Store create mode 100644 two_player_cleaning_game/assets/level_001.aseprite create mode 100644 two_player_cleaning_game/assets/level_001.png create mode 100644 two_player_cleaning_game/assets/level_002.aseprite create mode 100644 two_player_cleaning_game/assets/level_002.png create mode 100644 two_player_cleaning_game/assets/player_001.aseprite create mode 100644 two_player_cleaning_game/assets/player_001.png diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9b09888f19be03cef59d3a6fbc54e09d706ce959 GIT binary patch literal 10244 zcmeHMX^a#_6t361KsQUX%_Y0QGy@ZmW06@{2-o85>;mCj!pvU7G4@P1vmJW6$LXG# z#m&aWpeAbKf%3~l@cM;EOpHM#Vl)xOD~1@+#2@;DsL}WX|EjO5dI-C?CJ;;{bSG7> z->dhk=6zjX^;9(>1mY#VnUF9cM8ra+G6}1HxM({q*Ia^cp^bp?gcOKHG;)|2qY zUAWaytjFCQ}9PyDE6n z#JWlHWL54l56ZS&}Np64s1bFy_+pMmbKe)P0w`fzFyB5@Tz@}XzES5PSI;;{in2nTmn zQ}_D&G&gJ5x@qTn%BE{(EF)=ad1DGRNzw(San|fPAypmAwYQWp?=6|$VI?ApY>avH zRe7I|Iqr0HI*P8mDPli3`oj6Dx|g=J7Y(d+KVZpHRZhBylcM>K%cHWKJcN@@ap_OM z(3VxQ+GS}O!%}pKkF$22toD?7*2;F@xN(N8_Tq%dV~VbCi?^%l0Xpms&GkA9hK)bz zoImy!Y)iTQTG8mN;H3Ah+tJFNFxqL*Cf)QL)`)0yVitY0yEa{CIm~XHrKm}@TU5Rh z@kyI@*+in@v*3Iar3il_ghXOX_-*8@eS+OFq^DQgeMw)vbIBsIj>JhfQhksdCCAB0 za*Di9J|XAHx8z6i8@WvWgz-=Zlc63IXoR`27@A=nY=BnS2wNZt-OvNQpg|sNa9{w2 z;C>i}Bk%}34o|{Ua15S>=ixAt`hT2ZWqZ5K24;p~~_DBXbiTLjAgzH_I*i z%bRd!42Fi}t=qQm*tYXlV(7(s&dEDwHq2YNG`ec-#`u-F%O`tveqYTRMwJl+V^m=? zY1A~oz{x9ztHd77;x6QvF(6W^D-*bPL|z~w$CPngT_i6NkzLAot}Y8L7m?43z*Kof zs6|57Dm7eP7Fr{T3UFn;+$te|m0GToL9d<$jNkJ)*f-LB85OQEb5nL!i8R>BZ9)P3pAUq6DAU%%3({LP~gA;HP zUVu~Z3R2{CcmoOYE}Vh);2eAm=iyUEl`r8U{D4IHebnUNHEQxxQe5}s&pRC*{<~dV zF!PzFy0ag(-B>^RP3fo5fAuSuy?L)Yr*fibTaKN>JgN)DNT>oWKp;RMKp;RMK;ZU4 zpoXU!rsw~g#{T|)`v4MT8X#~RA^^1=iHSqN6_I36WC zjz@)_D=xx9ef=XMb|3R?!(6g(^kLyB`Dy-t|1qG5hnZs@ZwCASt4_^={r|rnj@`2R G|GxoST^3pZ literal 0 HcmV?d00001 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..074c301 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,169 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a LÖVE2D game development playground using Fennel (a Lisp dialect that compiles to Lua). The repository contains multiple game/project experiments: + +- **hello_world**: Basic LÖVE2D + Fennel template with REPL support +- **hello_world_lua**: Basic LÖVE2D in pure Lua (reference implementation) +- **one_line**: A pixel drawing application demonstrating canvas manipulation +- **two_player_cleaning_game**: Two-player game (primary focus for development) + +## Key Architecture + +### Fennel + LÖVE2D Setup + +Fennel is a Lisp-like language that compiles to Lua at runtime. Each project directory shares common bootstrap files via symlinks: + +- **fennel-1.5.3.lua** (root): The Fennel compiler itself +- **fennel_bootstrap.lua** (root): Sets up Fennel module loading and compiles `.fnl` files on-the-fly + +Each project directory contains: +- **main.fnl**: The Fennel source code (user-editable) +- **main.lua** → symlink to `../fennel_bootstrap.lua`: Entry point that Lua/LÖVE2D loads +- **fennel.lua** → symlink to `../fennel-1.5.3.lua`: Fennel compiler reference + +### How It Works + +1. LÖVE2D loads `main.lua` (the bootstrap file) +2. Bootstrap loads the Fennel compiler and sets up a custom module loader +3. Module loader intercepts `require("main.fnl")` and compiles it via Fennel at runtime +4. Compiled Fennel code executes with full LÖVE2D API access + +### LÖVE2D Lifecycle + +Games define lifecycle callbacks (all optional): +- **love.load()**: Called once at startup +- **love.update()**: Called every frame (~60fps by default) +- **love.draw()**: Called every frame after update, render graphics here +- **love.keypressed(key)**: Keyboard input handler +- **love.handlers.stdin(line)**: REPL input for live evaluation (used in hello_world template) + +## Development Workflow + +### Running a Game + +```bash +love ./two_player_cleaning_game +``` + +LÖVE2D must be installed and in your PATH. See [Getting Started](https://love2d.org/wiki/Getting_Started) for installation. + +### Live Coding / REPL + +The hello_world template includes stdin REPL support. While a game is running: +- Type Fennel code at the terminal +- Code is evaluated via `fennel.eval()` and results printed + +To enable in other projects, add this to `love.load()`: + +```fennel +(: (love.thread.newThread "require('love.event') +while 1 do love.event.push('stdin', io.read('*line')) end") :start) + +(fn love.handlers.stdin [line] + (let [(ok val) (pcall fennel.eval line)] + (print (if ok (fennel.view val) val)))) +``` + +## Fennel Language Basics + +### Syntax Patterns + +```fennel +; Variables +(local name "value") +(var counter 0) + +; Functions +(fn my-func [arg1 arg2] + (+ arg1 arg2)) + +; Tables/Objects +(local person { + :name "Alice" + :age 30 + :greet (fn [self] (print (.. self.name " says hello"))) +}) + +; Common control flow +(when condition + (do-something)) + +(if condition + (then-branch) + (else-branch)) + +(match value + "a" (handle-a) + "b" (handle-b)) + +; Iteration +(each [k v (pairs table)] + (print k v)) + +(for [i 1 10] + (print i)) +``` + +### Interop with Lua + +Call Lua functions naturally: + +```fennel +(love.graphics.setColor 1 0 0) ; red +(love.window.getMode) ; returns width, height, flags table +``` + +Access nested Lua objects with colon `:` syntax: + +```fennel +(: canvas-obj :setFilter "nearest" "nearest") ; equivalent to canvas_obj:setFilter(...) +``` + +## Project-Specific Notes + +### two_player_cleaning_game + +Currently uses the hello_world template structure. Development focus: +- Extend main.fnl with game logic (player movement, cleanup mechanics, etc.) +- Reference one_line/main.fnl for canvas/graphics patterns if needed +- Use LÖVE2D physics (love.physics) or collision detection as needed + +### Symlink Pattern + +Do **not** modify symlinked files (main.lua, fennel.lua) — modify main.fnl instead. If adding new modules, create them as separate .fnl files in the project directory. + +## Common Development Tasks + +### Adding a new module/file + +1. Create `my_module.fnl` in the project directory +2. Require it: `(local my-module (require "my_module"))` +3. The bootstrap module loader will auto-compile it + +### Debugging + +- Use `(print (fennel.view value))` to inspect tables and complex values +- stdin REPL (hello_world template) allows live evaluation for quick tests +- LÖVE2D console output is visible in the terminal where you ran `love` + +### Working with Graphics + +LÖVE2D graphics API examples: + +```fennel +(love.graphics.setColor 1 0 0) ; set color to red +(love.graphics.rectangle "fill" x y w h) ; draw filled rectangle +(love.graphics.circle "line" x y radius) ; draw circle outline +(love.graphics.print text x y) ; draw text +(love.graphics.draw drawable x y) ; draw canvas/image +``` + +## References + +- **LÖVE2D API**: https://love2d.org/wiki/Main_Page +- **Fennel Language**: https://fennel-lang.org/ +- **Fennel Plugin**: The compiler is shipped in this repo; no external install needed beyond LÖVE2D diff --git a/two_player_cleaning_game/.DS_Store b/two_player_cleaning_game/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ee089d426c1d0cd8b4c647ca55c310010e06d523 GIT binary patch literal 6148 zcmeHK!AiqG5Z$e{-BN@e6!aGGD%fHvf|pq94;aydN}HIX!8BW%)E-J9cl`i=#hZfQ z)sz3>A2_qS6=M}|MPw$-zTMfGO!g)0W*B3F!Ey+>*xtl5~X%! zZ9J}5>l;>myD{0Y#zg|V?W?dC$s5fihmTIllEEq5LMOAR@S^6N z%U)kQh_|ovJ=toRk5;@q-91E)&%3ie{;xj$%g=c_m@e}i*UlgcWpn}d>9eB`2{Awn z5Cd~&z-&RHJmY{yqb^KUkoMw!u`R+B%?7B>-R^%u1k-wFJgk25p0>MwkKN zIuuZca#Lb(9S(NO_}K;G&Jjfeqa;9oJo^DU>@z)P!#k_HGYIZM;*oBi%0P)s1mSS*a5T+ TrW(NlLO%kE25N|bxiat$W8`k) literal 0 HcmV?d00001 diff --git a/two_player_cleaning_game/assets/level_001.aseprite b/two_player_cleaning_game/assets/level_001.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..14f5fb171aa76dac854999f9e240ebbeef54691c GIT binary patch literal 586 zcmeZbVqkc%l#w9?2ss!S85n>xGEiV(1PL+-03j{FDWK`UKC*yqWdT|s2E^RMxDbTd5pdiAdU>0Vv= z21+2C6!?K84EzT&xEP!gle5!{@=NnlB!JFfRe;F;2ZkO5fWoh0&f8nre9a01ED6sA z?)+cnyLOwy^io!_=s7hWE0W<Px#U{Fj{MF0Q*|Ns9*C_H94F^^U|-*G0{pl0sFn)~uLndMMwzA!hGZcAw|QcEN< zJUKrd5qwX$2J1)FEjs zBvr?QH0kOOBLIRC>=TBZzU2y8J5-Xa68n#rRZ>z>))wl)MC8ecZwRVG?nFh<1L;yp z7-CE=4H5O=dx(3stwOw25FtW@NK526phqAum#IXE5E%>UGOP?V3fCf@Tk|wo3;BXd zTqTJiLS#rpd{*YWd6ce31u;8@_o$BJGXSIaM2P$&g5?^GMCS93CPau3Awq=6Ll8OQ zHuh~qIe~RnjzpGy`6h@Od9VERK7a=?*C}Fimv);Ru&*z42ankz*Ztz zmjz_AfLx~lWGE?^xN6v>SsM0-S>G>IySX6k-QiikKbg&Z8j!Wmz-oxbC|Kra-Ieq)j|NsAIuif5K*PfY`6&V&(=G3*u$mnKx^7ZOn{nNd= z@(q+gHYxA}Nf`JKWN|4a~?0UUl6bCMGdFu5#jxTV8# gK)LM^hlFaEOQ2&<#&&^h_71hXXCoLGs#V3y0dZJL0ssI2 literal 0 HcmV?d00001 diff --git a/two_player_cleaning_game/assets/level_002.png b/two_player_cleaning_game/assets/level_002.png new file mode 100644 index 0000000000000000000000000000000000000000..c5ae8290cf08733a8ffb9cdd978e39c121ff2e7d GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQajKx9jP7LeL$-D$|5(0ceT!FNb zf{Ck!O`4@)f0*_CLbaO<(%v1O_4||A%%=fa`wYyd!AP$0HF7-7c3v&j~E*g870SYNtO&Ffc@?h?hTKo_PzX Oi^0>?&t;ucLK6UaTu;#e literal 0 HcmV?d00001 diff --git a/two_player_cleaning_game/assets/player_001.aseprite b/two_player_cleaning_game/assets/player_001.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..80604a754689cf59df272c32b98f7f00990a7d7f GIT binary patch literal 493 zcmaFM$iVPmDImwW3RyLpoVnEEU0Jajz zIx8TX1>`!ABMhx3`k0t*^hovy2`LL*G$~BGx%GTQLq))pfU}c}`}bd+vwGw16Ap9E zxW9dNLJ6c(fgj1N|3EeugHvL1c6w2MX_CFI$ z6)3nW=KTI&pTW$=#wOyUTdZL?eM01t8J=q*kIe8~71?rT$+F5PvszD|nV5Djp+COp ztO{GeWu2t6D&hgB=Nx2W=(xpUSO7K~;#qXFtAD>a@_)NChp~adg5;70CI6<%76!-9 z96Pv|TXW8E*E*0c7uhhOvgOS2rE=46dzkXio~eA+ChIu6?j>%^G@(effU`i`mMr+Q PsLQgMm6_p}s;V6TnK5j| literal 0 HcmV?d00001 diff --git a/two_player_cleaning_game/assets/player_001.png b/two_player_cleaning_game/assets/player_001.png new file mode 100644 index 0000000000000000000000000000000000000000..fc97a2539ad54a6eeed9c2683e59918090f3a691 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjy#^NA%Cx&(BWL^R}MV>B>Ar`&K zHNW4mFz;;W%$du>!=rOTCQ*~SNoqo@*Tl0NXOBd5FYEC#3wHc`J6(N)n_O7oiZmtL zo@Esue$13PtFp9UN0OKcXN+QdsLaD0CEepQw{gC54{lJMD6n#JV`HP>^sE&BM?uZO f#)b`xD%coOcIeObG|rw5w4K4z)z4*}Q$iB}6z4vm literal 0 HcmV?d00001 diff --git a/two_player_cleaning_game/main.fnl b/two_player_cleaning_game/main.fnl index 4172050..1b7e742 100644 --- a/two_player_cleaning_game/main.fnl +++ b/two_player_cleaning_game/main.fnl @@ -1,4 +1,12 @@ +(var player-sprite nil) ; 20x20 pixels +(local game-state { + :player-pos [0 0] +}) + (fn love.load [] + (love.window.setMode 600 640) + + (set player-sprite (love.graphics.newImage "assets/player_001.png")) ;; start a thread listening on stdin (: (love.thread.newThread "require('love.event') while 1 do love.event.push('stdin', io.read('*line')) end") :start)) @@ -8,8 +16,34 @@ while 1 do love.event.push('stdin', io.read('*line')) end") :start)) (let [(ok val) (pcall fennel.eval line)] (print (if ok (fennel.view val) val)))) + +;; drawing +(lambda color [full-r full-g full-b] + (let [(r g b) (love.math.colorFromBytes full-r full-g full-b)] + [r g b] +)) + +(local off-white (color 237 230 200)) +(local black [0 0 0]) + +(fn draw-game-outline [] + (love.graphics.setColor (unpack black)) + (love.graphics.rectangle "line" 50 50 500 500)) + +(fn draw-player [] + (love.graphics.setColor 1 1 1) ; reset color to white (no tinting) + (love.graphics.draw player-sprite 50 50 0 1.25 1.25)) + (fn love.draw [] - (love.graphics.print "Hello from Fennel!\nPress any key to quit" 10 10)) + ;; clear the screen and set bg to off white + (love.graphics.clear) + (love.graphics.setColor (unpack off-white)) + (love.graphics.rectangle "fill" 0 0 600 640) + + (draw-player) + (draw-game-outline) +) + ; (love.graphics.print "Hello from Fennel!\nPress any key to quit" 10 10)) (fn love.keypressed [key] (love.event.quit))