Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions skills/odin/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2026 codevoyant contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
74 changes: 74 additions & 0 deletions skills/odin/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
name: odin
description: 'Odin programming language recipe lookup. Triggers on: "odin procedures", "odin allocators", "odin error handling", "odin unions", "odin strings", "odin dynamic arrays", "odin matrix math", "odin cli", "odin polymorphism", "how do I ... in odin", "odin recipe", "odin cheatsheet".'
license: MIT
compatibility: Works on Claude Code and any platform. No external tools required — a deterministic read-and-print reference.
argument-hint: '[topic]'
---

# odin

Fast recipe lookup for the [Odin](https://odin-lang.org) language. Each topic is a small self-contained file under `references/recipes/`. Resolve the query to **one** file, then **read it and print it verbatim** — do not summarize, re-derive, or add commentary. This is a lookup, not a synthesis task.

## Step 0: Parse query

```bash
QUERY="$*" # everything after /odin
```

## Step 1: Route to one recipe and print it

Resolve in this order, then `Read references/recipes/{slug}.md` and print its contents verbatim:

1. **Exact topic name.** Lowercase `QUERY` and replace spaces with hyphens. If it equals a recipe slug (`procedures`, `returns`, `structs`, `conversions`, `defer`, `allocators`, `errors`, `unions`, `data-structures`, `math`, `strings`, `terminal`, `polymorphism`, `cli`, `resources`), use that file. (So `/odin defer`, `/odin cli`, `/odin data structures` route directly.)
2. **Keyword match.** Otherwise pick the **first** row whose keyword appears in `QUERY` as a **whole word/phrase** (word-boundary, case-insensitive — never a bare substring, so `def` never matches `defer`).

| Keywords in query (whole-word) | Recipe file (`references/recipes/…`) |
|---|---|
| procedure, function, named return, default arg | `procedures.md` |
| return, tuple, multiple result, destructure | `returns.md` |
| struct, using, field, packed | `structs.md` |
| convert, conversion, cast, transmute, distinct, coerce | `conversions.md` |
| defer, cleanup, scope exit | `defer.md` |
| alloc, allocator, memory, free, delete, arena, context, temp, leak, tracking | `allocators.md` |
| error, or_return, or_else, result | `errors.md` |
| union, maybe, tagged, variant, type switch | `unions.md` |
| dynamic array, slice, map, append, data structure | `data-structures.md` |
| vector, matrix, linalg, math, dot, cross, normalize, swizzle | `math.md` |
| string, cstring, strconv, builder, rune, split, concat | `strings.md` |
| print, printf, terminal, tui, board, ansi, fullscreen, screen, color, fmt, stdin, input | `terminal.md` |
| polymorph, generic, parametric, where clause, proc group, any, overload | `polymorphism.md` |
| cli, command line, args, os.args, flags, main, build | `cli.md` |
| resource, learn, book, tutorial, link | `resources.md` |

**No query, or `list` / `index` / `help` / `topics`:** print the index below (do not read any recipe file).

**`all` / `everything` / `cheatsheet`:** print the index and say "pass a topic to print that recipe" — do not dump every file.

### Index (print for no-query)

```
odin — recipe lookup. Usage: /odin <topic>

procedures proc decl, default args, named returns
returns multiple returns & destructuring
structs struct literals, using, #packed
conversions cast / transmute / distinct; implicit vs explicit
defer defer & LIFO cleanup
allocators context allocator, temp, arena, tracking (freeing)
errors or_return / or_else, (value, Error) idiom
unions tagged unions, type switch, Maybe(T)
data-structures [dynamic]T, slices, map[K]V
math component-wise vectors, matrix[R,C]T, linalg
strings string/cstring, Builder, strconv, allocation notes
terminal fmt printing + full-screen ANSI board loop
polymorphism $T generics, where clauses, proc groups, any
cli os.args + core:flags
resources vetted docs, book, blogs, codebases

e.g. /odin allocators · /odin matrix math · /odin cli

Docs: https://odin-lang.org/docs/overview/
```

If a query matches nothing, print the index and ask which topic.
41 changes: 41 additions & 0 deletions skills/odin/references/recipes/allocators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Odin — allocators & freeing

`context.allocator` is implicit; most core procs take `allocator := context.allocator`. Pair every alloc with exactly one free.

```odin
p := new(int); defer free(p) // single value: new ↔ free
s := make([]int, 6); defer delete(s) // slice/map/dynarray/string: make ↔ delete

// Scratch memory: temp allocator + one bulk reclaim (no per-item delete)
for { // e.g. a frame loop
defer free_all(context.temp_allocator)
msg := fmt.tprintf("frame %d", i) // tprintf uses the temp allocator
scratch := make([]u8, 1024, context.temp_allocator)
_ = msg; _ = scratch
}

// Arena: bump-allocate into a fixed buffer, reset all at once
import "core:mem"
buf: [4096]u8
arena: mem.Arena
mem.arena_init(&arena, buf[:])
context.allocator = mem.arena_allocator(&arena)
// ... allocate freely ...
free_all(context.allocator) // resets the bump pointer
```

Leak detection (dev builds) — tracking allocator:

```odin
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track)
defer {
for _, e in track.allocation_map do fmt.eprintfln("leak %v @ %v", e.size, e.location)
mem.tracking_allocator_destroy(&track)
}
```

`free` = one pointer · `delete` = slice/map/dynarray/string · `free_all` = everything from an arena/temp at once (don't also free/delete those items — double free).

Docs: https://odin-lang.org/docs/overview/#implicit-context-system · https://pkg.odin-lang.org/core/mem/
36 changes: 36 additions & 0 deletions skills/odin/references/recipes/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Odin — CLI programs

`os.args` for raw args (`os.args[0]` is the program name); `core:flags` for declarative parsing.

```odin
package main
import "core:fmt"
import "core:os"

main :: proc() {
for arg, i in os.args do fmt.printf("[%d] %s\n", i, arg)
}
```

Declarative parsing with `core:flags` (struct + field tags):

```odin
import "core:flags"; import "core:os"

Options :: struct {
width: int `args:"name=w,required" usage:"board width"`,
name: string `usage:"player name"`,
verbose: bool `usage:"verbose"`,
file: string `args:"pos=0,required" usage:"input file"`,
}

main :: proc() {
opt: Options
flags.parse_or_exit(&opt, os.args) // prints usage + exits on -h or error
fmt.printf("w=%d name=%s\n", opt.width, opt.name)
}
```

`.Odin` style = `-w:8 -verbose`; pass `style = .Unix` for `--w=8`. No subcommands — dispatch on `os.args[1]` yourself, then parse the rest. Build: `odin build . -out:game` (or `odin run .`).

Docs: https://pkg.odin-lang.org/core/flags/
17 changes: 17 additions & 0 deletions skills/odin/references/recipes/conversions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Odin — type conversions

`T(x)` or `cast(T)x` to convert; `transmute` to reinterpret bits; `distinct` for a new nominal type.

```odin
i: int = 123
f := f64(i) // T(x) — explicit conversion
u := cast(u32)f // cast(T)x — same effect, different spelling
bits := transmute(u32)f32(1) // reinterpret bits (same size)

My_Int :: distinct int // new nominal type; My_Int != int, needs My_Int(x)
```

- **Implicit:** untyped constants (`X :: 42`, `1.0`, `"s"`) convert to any compatible target when exact — `x: f32 = 42`.
- **Explicit required:** between two *typed* values of different types (unlike C). `auto_cast v` forces it but is prototyping-only.

Docs: https://odin-lang.org/docs/overview/#type-conversion
30 changes: 30 additions & 0 deletions skills/odin/references/recipes/data-structures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Odin — dynamic arrays, slices, maps

Dynamic arrays grow (append/remove take `&arr`); slices are `low:high` (high exclusive); maps use comma-ok lookup.

```odin
// dynamic array
xs: [dynamic]int
defer delete(xs)
append(&xs, 1, 2, 3) // variadic
ordered_remove(&xs, 0) // keep order, O(n)
unordered_remove(&xs, 0) // swap last in, O(1)
last := pop(&xs)
for v, i in xs { fmt.println(i, v) } // value first, index second
clear(&xs) // len→0, keeps cap

// fixed array + slice (high bound exclusive)
fixed := [?]int{1, 2, 3, 4, 5} // [?] infers length
mid := fixed[1:4] // {2,3,4}
heap := make([]int, 6); defer delete(heap)

// map
m := make(map[string]int); defer delete(m)
m["bob"] = 2
v, ok := m["bob"] // comma-ok lookup
_ = "bob" in m // presence test
delete_key(&m, "bob")
for k, val in m { fmt.println(k, val) }
```

Docs: https://odin-lang.org/docs/overview/#dynamic-arrays · https://odin-lang.org/docs/overview/#maps
16 changes: 16 additions & 0 deletions skills/odin/references/recipes/defer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Odin — defer

`defer stmt` runs at scope exit, in **LIFO** order. Put cleanup right next to acquisition.

```odin
f, _ := os.open("x.txt")
defer os.close(f) // runs at scope exit

defer fmt.println("1") // LIFO → prints 3, 2, 1
defer fmt.println("2")
defer fmt.println("3")

defer { a(); b() } // defer a block
```

Docs: https://odin-lang.org/docs/overview/#defer
26 changes: 26 additions & 0 deletions skills/odin/references/recipes/errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Odin — error handling

Errors are plain values: return `(value, Error)` where `Error`'s zero value means success. `or_return` propagates; `or_else` supplies a default.

```odin
Error :: enum { None, Bad } // .None == success (zero value)

step :: proc() -> (int, Error) { return 1, .None }

run :: proc() -> (n: int, err: Error) {
n = step() or_return // if err != nil, return (0, err) now
n += 1
return
}

// propagate a file read, then default a map lookup
load :: proc(path: string) -> (data: []u8, err: os.Error) {
data = os.read_entire_file(path) or_return
return
}
port := config["port"] or_else 8080 // or_else = fallback value
```

See also: `unions.md` (`Maybe(T)` / `.?`).

Docs: https://odin-lang.org/docs/overview/#or_return-operator
34 changes: 34 additions & 0 deletions skills/odin/references/recipes/math.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Odin — vector & matrix math

Fixed arrays are component-wise; `matrix[R,C]T` is built in and `*` is real matrix multiply. Dot/cross/length live in `core:math/linalg`.

```odin
import "core:math/linalg"
Vec3 :: [3]f32

a := Vec3{1, 4, 9}
b := Vec3{2, 4, 8}
c := a + b // {3, 8, 17} component-wise
d := a * b // {2, 16, 72} component-wise (NOT dot)
e := a * 2 // scalar broadcast
xy := a.xy // swizzle

dp := linalg.dot(a, b) // scalar
cp := linalg.cross(a, b) // Vec3
n := linalg.normalize(a) // unit vector
len := linalg.length(a)

// matrices
m := matrix[2, 2]f32{1, 2, 3, 4}
v := [2]f32{5, 6}
mv := m * v // matrix × vector → [2]f32

id := linalg.MATRIX4F32_IDENTITY
T := linalg.matrix4_translate(Vec3{1, 2, 3})
// linalg also has matrix4_rotate(angle_rad, axis) / matrix4_scale; compose with `*`
world := T * id
```

Note: `matrix4_rotate` argument order is conventionally `(angle_radians, axis)` — confirm against your compiler's `pkg.odin-lang.org`.

Docs: https://odin-lang.org/docs/overview/#array-programming · https://pkg.odin-lang.org/core/math/linalg/
33 changes: 33 additions & 0 deletions skills/odin/references/recipes/polymorphism.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Odin — polymorphism

No methods/inheritance. Three tools: parametric generics (`$`), `where` constraints, and proc groups (overloading).

```odin
// $T inferred from args; works on any addable type
add :: proc(a, b: $T) -> T { return a + b }

// $T: typeid — pass a *type*; $N — compile-time constant
make_pair :: proc($T: typeid) -> [2]T { return {} }
first_n :: proc(s: []$E, $N: int) -> [N]E { r: [N]E; for i in 0..<N do r[i] = s[i]; return r }

// where clause constrains the params
import "base:intrinsics"
sum :: proc(s: []$T) -> T where intrinsics.type_is_numeric(T) {
total: T
for v in s do total += v
return total
}

// proc group = compile-time overloading, dispatched by arg types
show_int :: proc(x: int) { fmt.println("int", x) }
show_str :: proc(x: string) { fmt.println("str", x) }
show :: proc{show_int, show_str}
// show(3) → show_int ; show("hi") → show_str

// `any` = runtime dynamic value (ptr + typeid), for logging etc.
dump :: proc(v: any) { fmt.printf("%T = %v\n", v, v) }
```

Subtype-style reuse is `using` in a struct (see `structs.md`).

Docs: https://odin-lang.org/docs/overview/#parametric-polymorphism
26 changes: 26 additions & 0 deletions skills/odin/references/recipes/procedures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Odin — procedures

`name :: proc(params) -> ret { }`. Default args, named returns (naked `return` allowed), named args at call site.

```odin
package main
import "core:fmt"

add :: proc(a, b: int) -> int { return a + b }

// default args + named returns
stats :: proc(x: int, scale := 2) -> (lo, hi: int) {
lo = x
hi = x * scale
return
}

main :: proc() {
fmt.println(add(2, 3)) // 5
lo, hi := stats(4) // positional
_ = stats(x = 4, scale = 3) // named args at call site
fmt.println(lo, hi)
}
```

Docs: https://odin-lang.org/docs/overview/#procedures
37 changes: 37 additions & 0 deletions skills/odin/references/recipes/resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Odin — vetted resources

All links fetched/searched and confirmed live (2026-07). Authority noted.

## Official (start here)
- **Language Overview** — https://odin-lang.org/docs/overview/ — the canonical tour (syntax → types → procedures → the `context`/allocator system). Read end-to-end first.
- **Package docs** — https://pkg.odin-lang.org/ — browsable API for `base`/`core`/`vendor`.
- **FAQ / rationale** — https://odin-lang.org/docs/faq/ — why no methods, no exceptions, no package manager, manual memory. (There is no separate formal spec — Overview + FAQ + `core/` are it.)
- **`demo.odin`** — https://github.com/odin-lang/Odin/blob/master/examples/demo/demo.odin — one heavily-commented file covering nearly every feature. Skim after the overview.
- **`core/` source** — https://github.com/odin-lang/Odin/tree/master/core — best large idiomatic codebase; read it to learn conventions.
- **`examples/`** — https://github.com/odin-lang/Odin/tree/master/examples — small runnable snippets.
- **Repo / showcase** — https://github.com/odin-lang/Odin · https://odin-lang.org/showcase/

## Books
- **Understanding the Odin Programming Language — Karl Zylinski (2024, updated 2026)** — https://odinbook.com/ (buy: store.zylinski.se · zylinski.itch.io/odinbook). The definitive structured resource; use it as your spine. *(You already have it in `~/…/Books/Computer Science/Odin/`.)*

## Tutorials / blogs
- **gingerBill's articles** (Odin's creator) — https://www.gingerbill.org/article/ — especially the 6-part **"Memory Allocation Strategies"** series; internalizing this is the biggest shift from GC languages.
- **Karl Zylinski's blog** — https://zylinski.se/posts/ — practical patterns (arenas, handle-based maps, bindings). Free written intro: https://zylinski.se/posts/introduction-to-odin/
- **Karl Zylinski — YouTube** — https://www.youtube.com/@karl_zylinski — games without an engine, Odin + Raylib.
- **Odin News** — https://odin-lang.org/news/ — releases/announcements.

## Community
- **Discord** (primary hub) — https://discord.gg/vafXTdubwr
- **Forum** — https://forum.odin-lang.org/ · **community index** — https://odin-lang.org/community/
- **awesome-odin** — https://github.com/jakubtomsu/awesome-odin — curated libs/bindings/tutorials.

## Reference codebases (learn by reading)
- **Odin `core/`** — the canonical idioms (above).
- **Raylib hot-reload template — Karl Zylinski** — https://github.com/karl-zylinski/odin-raylib-hot-reload-game-template — standard game scaffold (shipped CAT & ONION).
- **karl2d** — https://github.com/karl-zylinski/karl2d — readable beginner 2D lib.
- **breakout / snake tutorials** — https://github.com/karl-zylinski/breakout · https://github.com/karl-zylinski/snake-tutorial-code — step-by-step games + videos.
- **Dungeon of Quake** — https://github.com/jakubtomsu/dungeon-of-quake — complete retro FPS.
- **VirtualXT** — https://github.com/virtualxt/virtualxt — PC/XT emulator (systems example).

## Suggested path (experienced programmer)
1. Read the **Overview** end to end. 2. Skim **demo.odin**. 3. Work through **Karl's book** (you own it). 4. Read gingerBill's **Memory Allocation Strategies**. 5. Read **`core/`** for idioms. 6. Build with the **Raylib hot-reload template**; keep **Discord** open.
Loading
Loading