TERMINAL GAMES
real games, written in Rust | played in your browser | nothing to install
These are the real games from this repository, compiled to WebAssembly and played right here. The pixel art is made of half-block characters, exactly as it is drawn in a real terminal.
loading the games ...
> THE GAMES
Keys work while the screen has focus, so click it first.
> THE TUNER
Drag a slider and the running game changes. Games choose which numbers to expose: see Tuning in the crate docs.
> THE SOURCE
This is examples/glow.rs in the repository. Run it with cargo run --example glow.
This is examples/sail.rs in the repository. Run it with cargo run --example sail.
This is examples/quickgame.rs in the repository. Run it with cargo run --example quickgame.
> MAKE YOUR OWN
Copy a game into src/main.rs and run it in your own terminal, with your own keyboard and mouse. A game is plain Rust: a Game trait with update and draw. To test one without a terminal, use Headless, which plays it for you.
$ cargo add terminal_graphics_interactive $ cargo run
//! QUICKGAME: steer a `@` with the arrows or WASD and collect the `*` stars
//! for 20 seconds. Enter starts, q quits, Ctrl+C always quits.
use terminal_graphics_interactive::game::*;
fn main() -> std::io::Result<()> {
run(Title { score: None })
}
/// The title (public so the website can start the game here). After a round it shows the score.
pub struct Title {
pub score: Option<u32>,
}
impl Game for Title {
fn update(&mut self, ctx: &mut Context<'_>) -> Next {
if ctx.keys.pressed(KeyCode::Enter) {
Next::switch(Play::new(ctx.seed))
} else if ctx.keys.pressed(KeyCode::Char('q')) {
Next::quit()
} else {
Next::stay()
}
}
fn draw(&self, screen: &mut Screen) {
screen.clear(Colour::Black);
screen.text_centred(8, "QUICKGAME", Colour::BrightYellow);
let line = match self.score {
Some(score) => format!("time up: {score} stars"),
None => "collect the stars before time runs out".to_string(),
};
screen.text_centred(10, &line, Colour::White);
screen.text_centred(12, "press Enter to play", Colour::BrightWhite);
}
}
/// One round: steer, collect stars, beat the clock.
struct Play {
rng: Rng,
x: f32,
y: f32,
score: u32,
time_left: f32,
stars: Pool<(i32, i32)>,
}
impl Play {
fn new(seed: u64) -> Play {
Play {
rng: Rng::new(seed),
x: 20.0,
y: 8.0,
score: 0,
time_left: 20.0,
stars: Pool::with_capacity(12),
}
}
}
impl Game for Play {
fn update(&mut self, ctx: &mut Context<'_>) -> Next {
if ctx.keys.pressed(KeyCode::Char('q')) {
return Next::quit();
}
let dx = ctx.keys.axis(KeyCode::Left, KeyCode::Right)
+ ctx.keys.axis(KeyCode::Char('a'), KeyCode::Char('d'));
let dy = ctx.keys.axis(KeyCode::Up, KeyCode::Down)
+ ctx.keys.axis(KeyCode::Char('w'), KeyCode::Char('s'));
// rows move half as fast: a cell is about twice as tall as it is wide
let (cols, rows) = (ctx.size.0 as i32, ctx.size.1 as i32);
let max_x = (cols - 1).max(0) as f32;
let max_y = (rows - 1).max(0) as f32;
self.x = (self.x + dx.clamp(-1, 1) as f32 * 24.0 * ctx.dt).clamp(0.0, max_x);
self.y = (self.y + dy.clamp(-1, 1) as f32 * 12.0 * ctx.dt).clamp(0.0, max_y);
let player = (self.x.round() as i32, self.y.round() as i32);
let before = self.stars.len();
self.stars.retain(|star| *star != player);
self.score += (before - self.stars.len()) as u32;
if ctx.tick % 36 == 0 {
let star = (self.rng.range_i32(0, cols), self.rng.range_i32(0, rows));
let _ = self.stars.push(star); // a full pool gives the star back
}
self.time_left -= ctx.dt;
if self.time_left <= 0.0 {
return Next::switch(Title {
score: Some(self.score),
});
}
Next::stay()
}
fn draw(&self, screen: &mut Screen) {
screen.clear(Colour::Black);
for &(x, y) in self.stars.iter() {
screen.put(x, y, '*', Colour::BrightYellow);
}
let (x, y) = (self.x.round() as i32, self.y.round() as i32);
screen.put(x, y, '@', Colour::BrightCyan);
let hud = format!("score {} time {}", self.score, self.time_left.ceil());
screen.text(0, 0, &hud, Colour::White);
}
}
> MULTIPLAYER
Multiplayer is not on this page yet. Two people can already share one keyboard in a game you write. Playing over a network is planned (lockstep over TCP first, then in the browser) and is not built.
> LINKS
- GitHub repository - source, examples, issues
- crates.io/crates/terminal_graphics_interactive
- docs.rs/terminal_graphics_interactive - API documentation