Files
kiln/kiln-core/src/tests/game_portals.rs
T
2026-08-11 23:01:12 -05:00

127 lines
4.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Board-to-board transitions via [`GameState::enter_board`] and portal stepping.
//!
//! Portals live in `Board::portals`, parallel to the grid rather than in it — the
//! player shares a cell with one — so `try_move` checks `portal_at` only after the
//! player has actually relocated.
use crate::board::Board;
use crate::board::tests::open_board;
use crate::game::GameState;
use crate::portal::Portal;
use crate::utils::{Direction, Point};
use crate::world::World;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
/// Builds a 3×3 board with the player at `player` and the given portals.
fn make_board(player: (usize, usize), portals: Vec<Portal>) -> Board {
let mut board = open_board(3, 3, player);
board.portals = portals;
board
}
/// A glyphless portal at `(x, y)` pointing at `target_name` on `target_board`.
fn portal(
x: usize,
y: usize,
name: &str,
target_board: &str,
target_name: &str,
) -> Portal {
Portal {
location: (x, y).into(),
name: name.to_string(),
target_board: target_board.to_string(),
target_name: target_name.to_string(),
glyph: None,
}
}
/// Two-board world used by all tests in this module, with b1's player at `b1_player`.
///
/// - `"b1"`: portal `"to_b2"` at `(2, 0)` → `"b2"` / `"from_b1"`
/// - `"b2"`: player at `(0, 0)`, portal `"from_b1"` at `(1, 1)` → `"b1"` / `"to_b2"`
fn two_board_world_with_player(b1_player: (usize, usize)) -> World {
let b1 = make_board(b1_player, vec![portal(2, 0, "to_b2", "b2", "from_b1")]);
let b2 = make_board((0, 0), vec![portal(1, 1, "from_b1", "b1", "to_b2")]);
World {
name: "test".into(),
start: "b1".into(),
torch: 10,
scripts: HashMap::new(),
boards: HashMap::from([
("b1".to_string(), Rc::new(RefCell::new(b1))),
("b2".to_string(), Rc::new(RefCell::new(b2))),
]),
}
}
/// The two-board world with b1's player at the origin.
fn two_board_world() -> World {
two_board_world_with_player((0, 0))
}
#[test]
fn enter_board_switches_board() {
let mut game = GameState::from_world(two_board_world());
game.enter_board("b2", "from_b1");
assert_eq!(game.current_board_name(), "b2");
}
#[test]
fn enter_board_places_player_at_arrival_portal() {
let mut game = GameState::from_world(two_board_world());
game.enter_board("b2", "from_b1");
// Arrival portal "from_b1" is at (1, 1) on b2.
assert_eq!(game.board().player_pos(), Point { x: 1, y: 1 });
}
#[test]
fn entering_a_board_leaves_exactly_one_player_on_it() {
// b2 is authored with a player at (0,0); arriving through the portal must
// *relocate* that player rather than stamping a second one, or the board ends
// up with two Tile::Player cells and player_pos() reports whichever comes
// first in row-major order.
let mut game = GameState::from_world(two_board_world());
game.enter_board("b2", "from_b1");
let b = game.board();
let players = (0..b.height)
.flat_map(|y| (0..b.width).map(move |x| (x, y)))
.filter(|&(x, y)| b.get((x, y)).as_ref().is_some_and(|t| t.player()))
.count();
assert_eq!(players, 1, "arriving on a board must not duplicate the player");
}
#[test]
fn enter_board_sets_board_transition() {
let mut game = GameState::from_world(two_board_world());
game.enter_board("b2", "from_b1");
assert_eq!(game.board_transition, Some(1.0));
}
#[test]
fn enter_board_unknown_board_logs_error() {
let mut game = GameState::from_world(two_board_world());
game.enter_board("nope", "entry");
assert_eq!(game.current_board_name(), "b1", "board should not change");
assert!(!game.log.is_empty(), "an error should be logged");
}
#[test]
fn enter_board_unknown_entry_logs_error() {
let mut game = GameState::from_world(two_board_world());
game.enter_board("b2", "nope");
assert_eq!(game.current_board_name(), "b1", "board should not change");
assert!(!game.log.is_empty(), "an error should be logged");
}
#[test]
fn try_move_onto_portal_switches_board() {
// Start the player one cell west of b1's portal at (2, 0).
let mut game = GameState::from_world(two_board_world_with_player((1, 0)));
game.try_move(Direction::East);
assert_eq!(game.current_board_name(), "b2");
}