Snake: Difference between revisions
Content added Content deleted
m (Craft Basic moved to the BASIC section.) |
m (→{{header|Rust}}: refactoring) |
||
Line 4,537: | Line 4,537: | ||
winsafe = "0.0.8" |
winsafe = "0.0.8" |
||
rand = "0.8.4" |
rand = "0.8.4" |
||
derive-new = "0.5" |
|||
*/ |
*/ |
||
#![windows_subsystem = "windows"] |
#![windows_subsystem = "windows"] |
||
use derive_new::new; |
|||
use rand::Rng; |
use rand::Rng; |
||
use std::{cell::RefCell, rc::Rc}; |
use std::{cell::RefCell, rc::Rc}; |
||
use winsafe::{co, gui, prelude::*, COLORREF, HBRUSH, HPEN, SIZE}; |
use winsafe::{co, gui, prelude::*, COLORREF, HBRUSH, HPEN, HWND, SIZE}; |
||
const STEP: i32 = 3; // px, motion per frame. STEP and FPS determine the smoothness and speed of the animation. |
const STEP: i32 = 3; // px, motion per frame. STEP and FPS determine the smoothness and speed of the animation. |
||
Line 4,553: | Line 4,555: | ||
const RATIO: i32 = CELL / STEP; |
const RATIO: i32 = CELL / STEP; |
||
const |
const STARTCELL: i32 = FIELD_W / 2 * RATIO; |
||
/// total field width (with overlap for collisions) in STEPs |
/// total field width (with overlap for collisions) in STEPs |
||
const TW: i32 = (FIELD_W + 2) * RATIO; |
const TW: i32 = (FIELD_W + 2) * RATIO; |
||
Line 4,565: | Line 4,567: | ||
S = TW, |
S = TW, |
||
} |
} |
||
use Direction:: |
use Direction::{Start, A, D, S, W}; |
||
#[derive(new)] |
|||
struct Context { |
struct Context { |
||
hwnd: HWND, |
|||
snake: Vec<i32>, // [ids_rect] where id_rect = y * TW + x (where x, y: nSTEPs) |
snake: Vec<i32>, // [ids_rect] where id_rect = y * TW + x (where x, y: nSTEPs) |
||
#[new(value = "[STARTCELL; 6]")] |
|||
id_r: [i32; 6], // ID 6 rectangles to color in next frame (bg, tail, turn, body, food, head) |
id_r: [i32; 6], // ID 6 rectangles to color in next frame (bg, tail, turn, body, food, head) |
||
#[new(default)] |
|||
gap: i32, |
gap: i32, // gap in STEPs between animation and logic cell (negative - remove tail) |
||
#[new(value = "Direction::Start")] |
|||
dir: Direction, |
dir: Direction, |
||
#[new(value = "Direction::S")] |
|||
ordered_dir: Direction, |
ordered_dir: Direction, |
||
} |
|||
impl Context { |
|||
fn new(wnd: gui::WindowMain, len: usize) -> Self { |
|||
⚫ | |||
⚫ | |||
snake: vec![START_CELL; len.saturating_sub(RATIO as usize)], |
|||
id_r: [START_CELL; 6], |
|||
⚫ | |||
⚫ | |||
ordered_dir: S, |
|||
⚫ | |||
⚫ | |||
} |
} |
||
pub fn main() { |
pub fn main() { |
||
⚫ | |||
let cells: Vec<i32> = |
|||
⚫ | |||
let [bg, tail, turn, body, food, head] = [0usize, 1, 2, 3, 4, 5]; |
let [bg, tail, turn, body, food, head] = [0usize, 1, 2, 3, 4, 5]; |
||
let mut colors = [(0x00, 0xF0, 0xA0); 6]; // color tail, turn, body |
let mut colors = [(0x00, 0xF0, 0xA0); 6]; // color tail, turn, body |
||
Line 4,604: | Line 4,603: | ||
}); |
}); |
||
⚫ | |||
⚫ | |||
let wnd = wnd.clone(); // WindowMain is based on Arc, so wnd.clone() is a shallow copy |
|||
let context = Rc::clone(&context); |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
wnd.on().wm_key_down({ |
wnd.on().wm_key_down({ |
||
Line 4,612: | Line 4,632: | ||
match (ctx.dir, k.char_code as u8) { |
match (ctx.dir, k.char_code as u8) { |
||
(Start, bt @ (b' ' | 113)) => { |
(Start, bt @ (b' ' | 113)) => { |
||
if bt == 113 { |
|||
ctx.snake.clear() // 113 == F2 key: restart without save |
|||
}; |
|||
ctx |
ctx.hwnd.InvalidateRect(None, true)?; // call .wm_paint() with erase |
||
ctx.hwnd.SetTimer(1, 1000 / FPS, None)?; |
|||
} |
} |
||
(W | S, bt @ (b'A' | b'D')) => ctx.ordered_dir = if bt == b'A' { A } else { D }, |
(W | S, bt @ (b'A' | b'D')) => ctx.ordered_dir = if bt == b'A' { A } else { D }, |
||
Line 4,625: | Line 4,646: | ||
}); |
}); |
||
wnd.on().wm_timer(1, { |
wnd.on().wm_timer(1, move || { |
||
let |
let mut ctx = context.borrow_mut(); |
||
let |
let new_h = ctx.id_r[head] + ctx.dir as i32; |
||
⚫ | |||
⚫ | |||
ctx.id_r[head] = new_h; |
|||
if ctx.gap < 0 { |
|||
ctx.id_r[bg] = ctx.snake.remove(0); |
|||
ctx.id_r[tail] = ctx.snake[0]; |
|||
ctx.id_r[ |
ctx.id_r[turn] = ctx.snake[RATIO as usize / 2]; |
||
} |
|||
ctx.gap -= ctx.gap.signum(); |
|||
if ctx.gap == 0 { |
|||
let hw = ctx.hwnd; |
|||
let eat = new_h == ctx.id_r[food]; |
|||
let mut snk_cells: Vec<_> = ctx.snake.iter().step_by(RATIO as usize).collect(); |
|||
⚫ | |||
if !eat && (cells.binary_search(&new_h).is_err() || snk_cells.contains(&&new_h)) { |
|||
⚫ | |||
hw.KillTimer(1)?; |
|||
hw.SetWindowText(&(hw.GetWindowText()? + " Restart: F2 (with save - Space)"))?; |
|||
*ctx = Context::new(hw, vec![STARTCELL; ctx.snake.len() - RATIO as usize]); |
|||
return Ok(()); |
|||
} else if eat || ctx.id_r[food] == STARTCELL && ctx.id_r[tail] != STARTCELL { |
|||
if eat { |
|||
hw.SetWindowText(&( |
hw.SetWindowText(&format!("Snake - Eaten: {}.", snk_cells.len()))?; |
||
} |
|||
if eat && snk_cells.len() == cells.len() - 2 { |
|||
hw.SetWindowText(&(hw.GetWindowText()? + " ALL !!!"))?; |
|||
ctx.id_r[food] = 0; // hide food: all eaten |
|||
} else if new_h != STARTCELL { |
|||
snk_cells.sort(); |
|||
ctx.id_r[food] = *(cells.iter()) |
|||
.filter(|i| **i != new_h && snk_cells.binary_search(i).is_err()) |
|||
.nth(rand::thread_rng().gen_range(0..cells.len() - snk_cells.len() - 1)) |
|||
.unwrap(); |
|||
snk_cells.sort(); |
|||
ctx.id_r[food] = *(cells.iter()) |
|||
.filter(|i| **i != new_h && snk_cells.binary_search(i).is_err()) |
|||
.nth(rand::thread_rng().gen_range(0..cells.len() - snk_cells.len() - 1)) |
|||
.unwrap(); |
|||
} |
|||
} |
} |
||
⚫ | |||
ctx.gap = if eat { RATIO } else { -RATIO } |
|||
} |
} |
||
ctx. |
ctx.dir = ctx.ordered_dir; |
||
ctx. |
ctx.gap = if eat { RATIO } else { -RATIO } |
||
Ok(()) |
|||
} |
|||
}); |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
winsafe::RECT { |
|||
left, |
|||
top, |
|||
⚫ | |||
bottom: top + SNAKE_W, |
|||
}, |
|||
⚫ | |||
)?; |
|||
} |
} |
||
ctx. |
ctx.snake.push(new_h); |
||
ctx.hwnd.InvalidateRect(None, false)?; // call .wm_paint() without erase |
|||
Ok(()) |
Ok(()) |
||
}); |
}); |
||
if let Err(e) = wnd.run_main(None) { |
if let Err(e) = wnd.run_main(None) { |
||
⚫ | |||
winsafe::HWND::NULL |
|||
⚫ | |||
⚫ | |||
} |
} |
||
}</syntaxhighlight> |
}</syntaxhighlight> |