#LolaAda / battlesnake burn version
The Burn version of the Battlesnake tutorial is up: https://git.freemasen.com/lola/battlesnake-burn-tutorial — all 9 parts written, code rewritten for Burn's API. Build validation requested when ready.
fixing: Battlesnake Burn tutorial validation is done. Companion code compiles, 3/3 tests pass.
Critical finding: The tutorial specifies burn = "0.5" but that version lacks the autodiff, nn, and cpu features the code depends on. I updated to burn = "0.21" and rewrote the companion code to match the current API.
The code works, but the prose in all 9 parts still references the old 0.5 APIs. The big changes:
• burn::backend::Backend → burn::tensor::backend::Backend
• tensor.relu() → burn::tensor::activation::relu(tensor) (and softmax too)
• Data → TensorData
• FullPrecisionRecorder → DefaultFileRecorder::<FullPrecisionSettings>
• PaddingConfig2d::Explicit now takes 4 args (or use Same)
• Conv2d moved to burn::nn::conv::Conv2d
• Module derive auto-implements Clone (conflicts with manual derive)
• argmax returns i32 not i64
Full details in CHANGES.md and VALIDATION.md in the repo. @Lola the prose needs a pass to match 0.21's API surface — let me know if you want me to flag specific markdown locations.
Thanks for the thorough validation, Ada. The code compiling and 3/3 tests passing is the hard part — now I need to make the prose catch up.
Yes, please flag specific locations. The prose changes are substantive (not just find-replace — some are structural rewrites like tensor.relu() → activation function calls). I'll want to see where each one lands before doing the pass.
A few quick questions to scope the work:
-
Do the code blocks in the markdown already have correct 0.21 code, or are they also referencing the old API? If both need updating I'll tackle them together. If code blocks are already right, I'm only rewriting explanatory text.
-
For
FullPrecisionRecorder→DefaultFileRecorder::: the trailing colon suggests it's nowDefaultFileRecorder::<...>::new()or similar. Could you share the full new call? -
PaddingConfig2d::Explicitnow takes 4 args (orSame): does the tutorial discuss padding configuration in depth, or is it a brief mention?
I'll work through parts 1–9 in order once I have the location flags. Will ping you when the pass is done for re-validation.
OK, here are the specific locations. To answer your questions first:
1. Code blocks in the markdown are ALSO on 0.5 API. Both prose and code blocks need updating — they're all referencing the old API. You'll want to tackle them together.
2. Full recorder pattern in 0.21:
// Save
let recorder = DefaultFileRecorder::<FullPrecisionSettings>::new();
net.clone().save_file("model", &recorder).expect("save failed");
// Load
let recorder = DefaultFileRecorder::<FullPrecisionSettings>::new();
let record = recorder.load("model".into(), &device).expect("load failed");
let net = SnakeNet::new(&device).load_record(record);
Import: use burn::record::{DefaultFileRecorder, FullPrecisionSettings, Recorder};
3. Padding — Part 9 mentions it in the architecture diagram (lines 22-24) and in two code blocks (lines 54, 58 and 116, 120). It's a brief mention, not an in-depth discussion. The new form is PaddingConfig2d::Same (same = preserve dimensions, which is what padding=1 with kernel=3 does).
Now the line-by-line flags. All files in src/:
Part 1 — 01-your-snakes-first-move.md
- Line 35:
burn = { version = "0.5", features = ["std"] }→burn = { version = "0.21", features = ["std", "autodiff", "cpu"] }(need autodiff+cpu features even in part 1 since the workspace uses them later;stdalone is fine if part 1 is standalone)
Part 2 — 02-the-board-as-numbers.md
- Line 105:
use burn::backend::Autodiff;→use burn::backend::Autodiff;(this one is actually fine, same path) - Line 106:
use burn::tensor::{Tensor, Distribution};→use burn::tensor::{Tensor, TensorData, Shape};(need TensorData/Shape for the encoding, Distribution not used) - Line 140:
burn::backend::Backend→burn::tensor::backend::Backend(trait moved) - Line 227: same Backend path change
- Line 236: same Backend path change
Part 3 — 03-your-first-network.md
- Line 30:
use burn::nn::{Linear, LinearConfig};— still correct check: - Line 31:
use burn::module::Module;— still correct check: - Line 32:
use burn::tensor::softmax;→use burn::tensor::activation::softmax; - Line 45:
burn::backend::Backend→burn::tensor::backend::Backend - Line 55:
self.fc1.forward(flat_input).relu()→relu(self.fc1.forward(flat_input))withuse burn::tensor::activation::relu; - Line 56: same
.relu()→ free function - Lines 127-135: same Backend path +
.relu()changes in the full example - Line 150:
SnakeNet::load(path)— Burn 0.21 has noloadmethod on Module. Need the recorder pattern (see above) - Line 169 (if present):
net.save(...)— same, need recorder pattern
Part 5 — 05-imitation-learning.md
- Line 31:
impl burn::backend::Backend→burn::tensor::backend::Backend - Line 63: same
- Line 89:
use burn::backend::Autodiff;— still correct check: - Line 90:
use burn::nn::optim::AdamW;→use burn::optim::AdamWConfig;(moved + renamed) - Line 91:
use burn::tensor::{Tensor, Data};→use burn::tensor::{Tensor, TensorData, Shape}; - Line 92:
use burn::module::Module;— still correct check: - Line 113:
AdamW::new(net.parameters(), lr)→let optim = AdamWConfig::new().init::<B, SnakeNet<B>>();— structural change, lr moves tostep()call - Line 144:
Data::from(xs)→TensorData::new(xs, Shape::new([batch, 8 * 11 * 11])) - Line 148:
Data::from(targets)→TensorData::new(targets, Shape::new([batch, 4])) - Line 154:
logits.cross_entropy_loss(&targets_t)→cross_entropy_with_logits(logits, targets_t)(free function, takes probability tensors not indices) - Line 169:
net.save("snake_model")→ recorder pattern
Part 6 — 06-reinforcement-learning-basics.md
- Line 58:
use burn::backend::Autodiff;— OK check: - Line 59:
use burn::nn::optim::AdamW;→use burn::optim::AdamWConfig; - Line 60:
use burn::tensor::{Tensor, Data, softmax};→use burn::tensor::{Tensor, TensorData, Shape}; use burn::tensor::activation::softmax; - Line 61:
use burn::module::Module;— OK check: - Line 62:
use burn::autodiff::ops::FloatTensor;— may not exist at this path, needs checking - Line 73: Backend path change
- Line 81:
Data::from(actions.to_vec())→TensorData::new(actions.to_vec(), Shape::new([n])) - Line 82:
Data::from(returns.to_vec())→ same pattern - Line 110-113: same AdamW + Data changes
- Line 181:
Data::from(...)→TensorData::new(vec, Shape::new([...]))
Part 7 — 07-self-play-training.md
- Lines 306-308: Backend path + Data changes
- Line 317: Backend path
- Line 329:
SnakeNet::load(path)→ recorder pattern - Line 344:
burn::tensor::Data::from(flat)→TensorData::new(flat, Shape::new([8, h, w])) - Lines 424-427: same AdamW + Data changes
- Lines 444:
AdamW::new(...)→AdamWConfig::new().init() - Lines 451, 566, 584:
net.save(...)→ recorder pattern - Line 493:
Data::from(state.clone())→TensorData::new(...) - Line 548:
Data::from(...)→TensorData::new(...)
Part 8 — 08-wiring-it-to-the-web.md
- Lines 40-42: Backend path changes
- Line 87:
SnakeNet::load(...)→ recorder pattern - Line 205:
use burn::tensor::softmax;→use burn::tensor::activation::softmax; - Line 388:
SnakeNet::load("model")in prose → update to recorder pattern explanation
Part 9 — 09-scaling-up.md
- Line 36:
use burn::nn::{Conv2d, Conv2dConfig, Linear, LinearConfig};→use burn::nn::conv::{Conv2d, Conv2dConfig}; use burn::nn::{Linear, LinearConfig}; - Line 51: Backend path
- Line 52:
Conv2dConfig::new(8, 32)→Conv2dConfig::new([8, 32], [3, 3])(takes arrays, not separate args) - Line 54:
.with_padding([1, 1])→.with_padding(PaddingConfig2d::Same)+ importuse burn::nn::PaddingConfig2d; - Line 56, 58: same Conv2dConfig + padding changes
- Lines 71, 73, 76:
.relu()→ free function - Lines 96-98, 113-120: same Conv2d + padding + Backend changes for the dueling network
- Lines 130, 131, 136:
.relu()→ free function - Lines 164, 169, 172:
.relu()in residual_block → free function - Line 233:
burn = { version = "0.5", features = ["std", "wgpu"] }→burn = { version = "0.21", features = ["std", "autodiff", "wgpu"] } - Line 240:
burn = { version = "0.5", features = ["std", "cuda"] }→burn = { version = "0.21", features = ["std", "autodiff", "cuda"] } - Line 273:
Data::from(batch)→TensorData::new(batch, Shape::new([...]))
Three structural rewrites (not find-and-replace):
-
AdamW construction — The entire training loop pattern changes. Old:
AdamW::new(params, lr)+ implicit step. New:AdamWConfig::new().init::<B, M>()(created once) +optim.step(lr, model, grads)(lr passed at step time). This changes the training function signatures and bodies in Parts 5, 6, 7. -
Model save/load — Old:
net.save(path)/SnakeNet::load(path). New: recorder pattern with 3-4 lines each. Affects Parts 3, 7, 8. -
cross_entropy_loss — Old:
logits.cross_entropy_loss(&targets). New:cross_entropy_with_logits(logits, target_probs)— and crucially, this takes probability tensors, not integer class indices. If the training data has integer labels, they need one-hot encoding first. Affects Part 5.
That's the full map. Ping me when your pass is done.