Snake: Difference between revisions

2,080 bytes added ,  3 months ago
m
(Applesoft BASIC)
 
(17 intermediate revisions by 6 users not shown)
Line 1,155:
=={{header|BASIC}}==
==={{header|Applesoft BASIC}}===
Rather than using DIM X%(1599),Y%(1599) which takes a long time to initialize, instead to speed this up, the program will POKE and PEEK the snake coordinates in memory. On initialization, LOMEM: 24576 moves variables out of the way. If you quit or it's game over, you can continue playing by typing the CONT command from the Applesoft BASIC prompt.
 
Continue playing by typing the CONT command from the Applesoft BASIC prompt if you quit or it's game over.
<syntaxhighlight lang="basic">REM === VARIABLES ===
REM
Line 1,169 ⟶ 1,171:
REM E Y-DELTA
REM F FOOD COLOR= 4 DARK GREEN
REM G ADDRESS OF 128 KEYCODE BEHESTS
REM H HIT= 127
REM I KEYBOARD= 49152
REM J CLEAR KEYBOARD= 49168
REM K KEYCODE
REM L <UNUSED>LENGTH WIDTH&HEIGHT= 40
REM M MAX= 1599 UPPER BOUND OF SNAKE X%() AND Y%() ARRAYSCOORDINATES
REM N NEW POSITION OF HEAD OF SNAKE
REM O OLD POSITION OF TAIL OF SNAKE
Line 1,193 ⟶ 1,194:
REM === KEYBOARD ===
REM
REM UPPER LOWER KEY BEHEST
REM 155 ESC QUIT
REM 139 UP UP
REM 193 225 A UP
REM 201 233 I UP
REM 138 DOWN DOWN
REM 218 250 Z DOWN
REM 203 235 K DOWN
REM 136 <- LEFT LEFT
REM 202 234 J LEFT
REM 149 -> RIGHT RIGHT
REM 204 236 L RIGHT
 
0 ON B = Q GOTO 5: IF B THEN D = D(B):E = E(B)
1 X = X + D:Y = Y + E:C = SCRN( X,Y): COLOR= S: PLOT X,Y: COLOR= R: PLOT FN X(N), FN Y(N):N = N - T + (N = 0) * M: POKE P + N,X:: POKE Z + N,Y: ON C(C) GOTO 4: IF NOT C THEN COLOR= 0: PLOT FN X(O), FN Y(O):O = O - T + (O = 0) * M
2 IF C THEN U = INT ( RND (1T) * 40L):V = INT ( RND (1T) * 40L): ON SCRN( U,V) > 0 GOTO 2: COLOR= F: PLOT U,V
3 K = PEEK (I):B = PEEK (J * (K > H)):B = K(K): GOTO
4 COLOR= T: PLOT X,Y: READ Q$: DATA5,20,20,1,-1,1,-1,13,9,4,10,1,40,1599,127,49152,49168,4608,6400,5,4,4,4,4,4,3,3,3,3,3,2,2,2,1,1,1,DONE,GAME OVER
5 IF Q = 5 THEN PRINT Q$;: END : HOME :Q = Q * (B = Q):B = 0: ON Q = 5 GOTO : RUN
6 LOMEM: 24576
7 DIM K(255),D(4),E(4),C(15): READ Q,X,Y,D(1),D(2),E(3),E(4),S,R,F,W,T,L,M,H,I,J,P,Z,K(155),K(139),K(193),K(225),K(201),K(233),K(138),K(218),K(250),K(203),K(235),K(136),K(202),K(234),K(149),K(204),K(236),Q$
8 DEF FN X(I) = PEEK (P + I): DEF FN Y(I) = PEEK (Z + I):B = RND (0):C(S) = T:C(R) = T:C(W) = T:B = INT ( RND (T) * 4) + T:D = D(B):E = E(B): POKE P + O,X: POKE Z + O,Y
9 GR : HOME : COLOR= W: VLIN 0,39 AT 0: VLIN 0,39 AT 39: HLIN 1,38 AT 0: HLIN 1,38 AT 39: COLOR= F: PLOT X + D,Y + E: GOTO</syntaxhighlight>
 
==={{header|Craft Basic}}===
[[File:Craft Basic Snake.png|right]]
Line 1,219 ⟶ 1,223:
rem by Gemino Smothers 2022
rem www.lucidapogee.com
 
title "Snake!"
 
define gfxx = 330, gfxy = 296
Line 1,233 ⟶ 1,239:
let sy[0] = gfxy / 2
 
fill on
title "Snake!"
bgcolor 128, 64, 0
cls graphics
 
resize 0, 0, gfxx + 10, gfxy + 56
center
 
formid 1
fill on
staticform 1, 1, 100, 14
bgcolor 128, 64, 0
fgcolor 255, 255, 0
cls
bgcolor 0, 80, 0
colorform
 
alert "Snake! by Gemino Smothers 2022"
Line 1,364 ⟶ 1,374:
 
fgcolor 255, 255, 0
cursor 1,formid 1
printformtext "Score: ", score
updateform
 
let delay = clock
Line 1,378 ⟶ 1,389:
 
playwave "examples\boom.wav"
alert "Game over! Score: ", score</syntaxhighlight>
 
alert "Game over! Score: ", score
 
end</syntaxhighlight>
 
==={{header|FreeBASIC}}===
Line 1,502 ⟶ 1,510:
</syntaxhighlight>
 
==={{header|Integer BASIC}}===
{{Trans|Applesoft BASIC}}
<syntaxhighlight lang="basic"> 0 IF B=Q THEN GOTO 9: IF B THEN D=D(B): IF B THEN E=E(B):X=X+D:Y=Y+E:A= SCRN(X,Y): COLOR=S: PLOT X,Y: COLOR=R: PLOT X(N),Y(N)
1 N=N-T+(N=1)*M:X(N)=X:Y(N)=Y: IF C(A) THEN GOTO 9: IF A THEN GOTO 2: COLOR=0: PLOT X(O),Y(O):O=O-T+(O=1)*M: GOTO 3
2 U= RND (L):V= RND (L): IF SCRN(U,V)>0 THEN GOTO 2: COLOR=F: PLOT U,V
3 K= PEEK (I):B= PEEK (J*(K>H)):B=K(K): GOTO 0
4 DIM X(1600),Y(1600),K(255),D(4),E(4),C(15): FOR I=0 TO 255:K(I)=0: NEXT I: FOR I=1 TO 4:D(I)=0:E(I)=0: NEXT I
5 FOR I=0 TO 15:C(I)=0: NEXT I:Q=5:X=20:Y=20:E(1)=-1:D(2)=-1:E(3)=1:D(4)=1:S=13:R=9:F=4:W=10:T=1:L=40:M=1600
6 H=127:I=-16384:J=-16368:K(155)=5:K(193)=1:K(225)=1:K(201)=1:K(233)=1:K(136)=2:K(202)=2:K(234)=2:N=1:O=1
7 K(218)=3:K(250)=3:K(203)=3:K(235)=3:K(149)=4:K(204)=4:K(236)=4:C(S)=T:C(R)=T:C(W)=T:B= RND (4)+T:D=D(B)
8 E=E(B):X(O)=X:Y(O)=Y: GR : CALL -936: COLOR=W: VLIN 0,39 AT 0: VLIN 0,39 AT 39: HLIN 1,38 AT 0: HLIN 1,38 AT 39: COLOR=F: PLOT X+D,Y+E: GOTO 0
9 IF Q#5 THEN GOTO 4:Q=Q*(B=Q): COLOR=T: IF Q=0 THEN PLOT X,Y: IF Q=0 THEN PRINT "GAME OVER";: IF Q THEN PRINT "DONE";:K= PEEK (J):B=0: END </syntaxhighlight>
==={{header|Locomotive Basic}}===
 
Line 2,270 ⟶ 2,290:
OnCreate = FormCreate
end
</syntaxhighlight>
 
=={{header|EasyLang}}==
[https://easylang.dev/apps/snake.html Run it]
{{Trans|Craft Basic}}
<syntaxhighlight>
subr fruit
rx = (randint 20 - 1) * 5 + 2.5
ry = (randint 20 - 1) * 5 + 2.5
.
subr start
fruit
game = 1
sx[] = [ 52.5 0 0 0 0 ]
sy[] = [ 52.5 0 0 0 0 ]
dir = randint 4
timer 0
.
background 242
move 30 70
clear
color 997
text "SNAKE"
textsize 5
move 6 40
text "Keys or mouse for controlling"
move 6 30
text "Space or click to to start"
#
on key
if game = 0 and keybkey = " "
start
return
.
if dir mod 2 = 1
if keybkey = "ArrowRight"
dir = 2
elif keybkey = "ArrowLeft"
dir = 4
.
else
if keybkey = "ArrowUp"
dir = 1
elif keybkey = "ArrowDown"
dir = 3
.
.
.
on mouse_down
if game = 0
start
return
.
if dir mod 2 = 1
if mouse_x < sx
dir = 4
else
dir = 2
.
else
if mouse_y < sy
dir = 3
else
dir = 1
.
.
.
on timer
clear
color 997
move 2 95
text "Score: " & 10 * len sx[] - 50
color 966
move rx ry
circle 1.5
#
sx = sx[1] ; sy = sy[1]
if dir = 1
sy += 5
elif dir = 2
sx += 5
elif dir = 3
sy -= 5
elif dir = 4
sx -= 5
.
if sx < 0 or sx > 100 or sy < 0 or sy > 100
game = 0
.
color 494
for i = len sx[] downto 2
if sx = sx[i] and sy = sy[i]
game = 0
.
sx[i] = sx[i - 1]
sy[i] = sy[i - 1]
if sx[i] > 0
move sx[i] sy[i]
circle 2.5
.
.
move sx sy
circle 2.5
color 000
if dir = 2 or dir = 4
move sx sy + 1
circle 0.5
move sx sy - 1
circle 0.5
else
move sx + 1 sy
circle 0.5
move sx - 1 sy
circle 0.5
.
if sx = rx and sy = ry
len sx[] len sx[] + 3
len sy[] len sy[] + 3
fruit
.
sx[1] = sx ; sy[1] = sy
if game = 1
timer 0.15
else
color 997
move 10 10
text "Space or click new game"
.
.
</syntaxhighlight>
 
Line 3,141 ⟶ 3,290:
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">using GLMakie, GeometryBasics
Makie version in 99 lines.
<syntaxhighlight lang="julia">using Makie
 
mutable struct SnakeGame
height::Int
width::Int
snake::Vector{CartesianIndex{2}}
food::CartesianIndex{2}
end
 
Line 3,187 ⟶ 3,335:
 
function play(;n=10,t=0.5)
game = NodeObservable(SnakeGame(;width=n,height=n))
scene = Scene(resolution = (1000, 1000), raw = true, camera = campixel!)
display(scene)
Line 3,208 ⟶ 3,356:
 
score_text = @lift("Score: $(length($game.snake)-1)")
text!(scene, score_text, color=:gray, position = @lift((widths($area)[1]/2, widths($area)[2])), textsizefontsize = 50, align = (:center, :top))
 
direction = Ref{Any}(nothing)
 
on(events(scene).events.keyboardbuttonskeyboardbutton) do but
if ispressed(but,.action == Keyboard.press || but.action == Keyboard.left)repeat
direction[]if but.key == CartesianIndex(-1,0)Keyboard.left
direction[] = CartesianIndex(-1,0)
elseif ispressed(but, Keyboard.up)
direction[]elseif but.key == CartesianIndex(0,1)Keyboard.up
direction[] = CartesianIndex(0,1)
elseif ispressed(but, Keyboard.down)
direction[]elseif but.key == CartesianIndex(0,-1)Keyboard.down
direction[] = CartesianIndex(0,-1)
elseif ispressed(but, Keyboard.right)
direction[]elseif but.key == CartesianIndex(1,0)Keyboard.right
direction[] = CartesianIndex(1,0)
end
end
end
Line 4,587 ⟶ 4,737:
=={{header|Rust}}==
Implemented smooth (per-pixel) animation on Win32 API (tested on Windows 7 and Windows 11)
 
Used winsafe - a safe rust bindings library for Win32 GUI: young but very handy, with links to docs.microsoft.com from doc and src for all Win32 entities involved.
 
Along the way, the possibility of restarting while maintaining the length of the snake has been implemented. Now a long snake is available to everyone!
 
[http://github.com/rust-rosetta/rust-rosetta/blob/master/tasks/snake/SnkRust.png snake game screenshot]
<syntaxhighlight lang="rust">/* add to file Cargo.toml:
[dependencies]
winsafe = "0.0.8" # IMHO: before the appearance of winsafe="0.1" it is not worth raising the version here
winsafe = "0.0.8"
rand = "0.8.4"
derive-new = "0.5"
*/
Line 4,603 ⟶ 4,747:
 
use derive_new::new;
use rand::{thread_rng, Rng};
use std::{cell::RefCell, rc::Rc};
use winsafe::{co, gui, prelude::*, COLORREF, HBRUSH, HPEN, HWNDRECT, SIZE};
 
const STEP: i32 = 3; // px, motion per frame. STEP and FPS determine the smoothness and speed of the animation.
const FPSGCW: u32i32 = 907; // game grid cell width in STEPs
const CELL: i32 = 21; // px, game grid (logical step). Will be aligned by STEP
const FIELD_W: i32 = 20; // width of the square field in CELLs
const SNAKE_W: i32 = 20; // px
const FW: i32 = 20; // the width of the square field in the cells of the game grid
const ROUNDING: SIZE = SIZE::new(SNAKE_W / 2, SNAKE_W / 2);
const TW: i32 = (FW + 2) * GCW; // total field width (with overlap for collisions) in STEPs
 
const RATIOID0: i32 = CELLFW / STEP2 * GCW; // starting position id
const STARTCELL: i32 = FIELD_W / 2 * RATIO;
/// total field width (with overlap for collisions) in STEPs
const TW: i32 = (FIELD_W + 2) * RATIO;
#[derive(Clone, Copy)]
#[repr(i32)]
enum Direction {
Start = 0,
A = -1,
D = 1,
W = -TW,
S = TW,
}
use Direction::{Start, A, D, S, W};
 
#[rustfmt::skip]
#[derive(new)]
struct Context {
wnd: gui::WindowMain,
hwnd: HWND,
#[new(default) ] snake: Vec<i32>, // [ids_rectrect_ids] where id_rectrect_id = y * TW + x (where x, y: nSTEPs)
#[new(value = "[STARTCELLID0; 6]")] r: [i32; 6], // ID 6 rect to color in next frame (bg, tail, turn, body, food, head)
#[new(default) ] incr: i32, // 0 | -1 | 1 | -TW | TW - increment r[head] in next STEP
id_r: [i32; 6], // ID 6 rectangles to color in next frame (bg, tail, turn, body, food, head)
#[new(value = "TW") ] next_incr: i32, // `incr` in the next grid cell
#[new(default)]
#[new(default) ] gap: i32, // gapinterval in STEPs betweento animationthe andnext logicgrid cell; (negative - remove tail) clipping mark
#[new(value = "Direction::Start")]
dir: Direction,
#[new(value = "Direction::S")]
ordered_dir: Direction,
}
 
pub fn main() {
let [bg, tail, turn, body, food, head] = [0_usize, 1, 2, 3, 4, 5];
let context = Rc::new(RefCell::new(Context::new(HWND::NULL, Vec::new())));
let grid: Vec<_> = (1..=FW).flat_map(|y| (1..=FW).map(move |x| (y * TW + x) * GCW)).collect();
let cells: Vec<i32> =
(1..=FIELD_W).flat_map(|y| (1..=FIELD_W).map(move |x| (y * TW + x) * RATIO)).collect();
let [bg, tail, turn, body, food, head] = [0usize, 1, 2, 3, 4, 5];
let mut colors = [(0x00, 0xF0, 0xA0); 6]; // color tail, turn, body
colors[bg] = (0x00, 0x50, 0x90);
Line 4,657 ⟶ 4,780:
let wnd = gui::WindowMain::new(gui::WindowMainOpts {
title: "Snake - Start: Space, then press W-A-S-D".to_string(),
size: winsafe::SIZE::new(FIELD_WFW * RATIOGCW * STEP, FIELD_WFW * RATIOGCW * STEP),
ex_style: co::WS_EX::CLIENTEDGE,
class_bg_brush: brushes[bg],
..Default::default()
});
// WindowMain is based on Arc, so wnd.clone() is a shallow copy of a reference.
let context = Rc::new(RefCell::new(Context::new(wnd.clone())));
 
wnd.on().wm_paint({
let wnd = wnd.clone(); // WindowMain is based on Arc, so wnd.clone() is a shallow copy
let context = Rc::clone(&context);
move || {
let mut ctx = context.borrow_mutborrow();
ctx.hwnd = wnd.hwnd();
let mut ps = winsafe::PAINTSTRUCT::default();
let hdc = ctx.wnd.hwnd().BeginPaint(&mut ps)?;
hdc.SelectObjectPen(HPEN::CreatePen(co::PS::NULL, 0, COLORREF::new(0, 0, 0))?)?;
for (&id_rectid, &brush) in ctx.id_rr.iter().zip(&brushes[bg..=head]) {
let [left, top] = [id % TW, id / TW].map(|i| i * STEP - (STEP * GCW + SNAKE_W) / 2);
let rect = RECT { left, top, right: left + SNAKE_W, bottom: top + SNAKE_W };
hdc.SelectObjectBrush(brush)?;
let left = id_rect % TW * STEP -hdc.RoundRect(rect, SIZE::new(STEPSNAKE_W */ RATIO +2, SNAKE_W) / 2))?;
let top = id_rect / TW * STEP - (STEP * RATIO + SNAKE_W) / 2;
hdc.RoundRect(
winsafe::RECT { left, top, right: left + SNAKE_W, bottom: top + SNAKE_W },
ROUNDING,
)?;
}
Ok(ctx.wnd.hwnd().EndPaint(&ps);)
Ok(())
}
});
Line 4,688 ⟶ 4,806:
wnd.on().wm_key_down({
let context = Rc::clone(&context);
move |kkey| {
let mut ctx = context.borrow_mut();
Ok(match (ctx.dirincr.abs(), kkey.char_code as u8) {
(Start0, bt @ (b' ') |=> 113_ = ctx.wnd.hwnd().SetTimer(1, 10, None)?, =>// Start / {Restart
(TW, bt @ (b'A' | b'D')) => ctx.next_incr = if bt == 113b'A' { -1 } else { 1 },
(1, bt @ (b'S' | b'W')) => ctx.snake.clear()next_incr //= 113if bt == F2b'S' { TW } key:else restart{ without-TW save},
};
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 },
(A | D, bt @ (b'S' | b'W')) => ctx.ordered_dir = if bt == b'S' { S } else { W },
_ => (),
})
Ok(())
}
});
Line 4,708 ⟶ 4,819:
wnd.on().wm_timer(1, move || {
let mut ctx = context.borrow_mut();
let new_h = ctx.id_rr[head] + ctx.dir as i32incr;
(ctx.id_rr[body], ctx.r[head]) = (ctx.id_rr[head], new_h);
ctx.id_r[head] = new_h;
if ctx.gap < 0 {
ctx.id_rr[bg] = ctx.snake.remove(0);
ctx.id_rr[tail] = ctx.snake[0];
ctx.id_rr[turn] = ctx.snake[RATIOGCW as usize / 2];
}
ctx.gap -= ctx.gap.signum();
if ctx.gap == 0 {
letctx.gap hw= if new_h == ctx.hwndr[food] { GCW } else { -GCW };
let eatmut =snake_cells: new_hVec<_> == ctx.id_r[food]snake.iter().step_by(GCW as usize).collect();
letif mut snk_cells: Vec<_>new_h == ctx.snake.iter().step_by(RATIOr[food] as usize).collect();{
ctx.wnd.hwnd().SetWindowText(&format!("Snake - Eaten: {}", snake_cells.len()))?;
if !eat && (cells.binary_search(&new_h).is_err() || snk_cells.contains(&&new_h)) {
hwsnake_cells.KillTimersort(1)?;
hwctx.SetWindowTextr[food] = *(&(hwgrid.GetWindowTextiter()? + " Restart: F2 (with save - Space)"))?;
*ctx = Context::new(hw, vec![STARTCELL; ctx.snake.lenfilter()|i| -**i RATIO!= asnew_h usize]&& snake_cells.binary_search(i).is_err());
.nth(thread_rng().gen_range(0..(grid.len() - 1 - snake_cells.len()).max(1)))
.unwrap_or(&0);
} else if grid.binary_search(&new_h).is_err() || snake_cells.contains(&&new_h) {
ctx.wnd.hwnd().KillTimer(1)?; // Stop
let title = ctx.wnd.hwnd().GetWindowText()?;
ctx.wnd.hwnd().SetWindowText(&(title + ". Restart: Space"))?;
*ctx = Context::new(ctx.wnd.clone());
return Ok(());
} else if eat || ctx.id_r[food] == STARTCELL && ctx.id_r[tail] != STARTCELL {
if eat {
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();
}
}
ctx.dirincr = ctx.ordered_dirnext_incr;
ctx.gap = if eat { RATIO } else { -RATIO }
}
ctx.snake.push(new_h);
ctx.wnd.hwnd().InvalidateRect(None, falsenew_h == ID0)?; // call .wm_paint(), withoutwith erase on Restart
Ok(())
});
 
if let Err(e) = wnd.run_main(None) {.unwrap();
HWND::NULL.MessageBox(&e.to_string(), "Uncaught error", co::MB::ICONERROR).unwrap();
}
}</syntaxhighlight>
{{out}}
[[File:Snake rust.png]]
 
=={{header|Sidef}}==
Line 5,018 ⟶ 5,120:
{{libheader|Wren-dynamic}}
An embedded program so we can ask the C host to call ncurses and another library function for us.
<syntaxhighlight lang="ecmascriptwren">/* snakeSnake.wren */
 
import "random" for Random
Line 5,156 ⟶ 5,258:
<br>
Now embed this script in the following C program, compile and run it.
<syntaxhighlight lang="c">/* gcc snakeSnake.c -o snakeSnake -lncurses -lwren -lm */
 
#include <stdio.h>
Line 5,302 ⟶ 5,404:
WrenVM* vm = wrenNewVM(&config);
const char* module = "main";
const char* fileName = "snakeSnake.wren";
char *script = readFile(fileName);
WrenInterpretResult result = wrenInterpret(vm, module, script);
1,969

edits