/*cargo add getch-rs random rand*/const FW:usize=12;const FH:usize=20;const BW:usize
=4;const BS:usize=BW*BW;use getch_rs::{ Getch,Key as K};use rand::Rng;use std::sync
::{Arc,Mutex};use std::{thread as th, time };fn main()->std::io::Result<()>{
let c=Arc::new(Mutex::new(Ctx::new ()) ); println!("\x1b[2J\x1b[H\x1b[?25l"
);c.lock().unwrap().d();{let c=Arc :: clone (&c);let _=th::spawn(move||{loop
{th::sleep(time::Duration :: from_millis(1000));let mut c=c.lock(
).unwrap();let np=P{x:c.p .x,y:c.p.y+1,};if!c.collide
(&np){c.p=np;}else{ c.fix ();c. el();if c.sb(){break
;}}c.d()}});}let g= Getch :: new ();loop{match g.
getch()?{K::Left=>{ let mut c=c.lock().
unwrap();let np=P{x :c.p .x.checked_sub(1).
unwrap_or(c.p.x),y: c.p. y,};c.mv(np);}K::
Right=>{let mut c =c . lock (). unwrap();let
np=P{x:c.p.x +1,y:c .p.y,} ;c.mv(np);}K
::Down=>{let mut c=c.lock ().unwrap( );let np=P{x:
c.p.x,y:c.p.y +1,};c.mv(np) ;}K::Char(' ' )|K::Char('x'
)=>{let mut c =c.lock().unwrap ();c.rr();}K:: Char('z')=>{let
mut c=c.lock( ).unwrap();c.rl();}K::Esc|K::Char('q' )=>{break;}_=>
(),}}Ok(quit( ))}struct Ctx{f:[usize;FW*FH],p:P,bk:[ usize;BS],}impl
Ctx{fn new()->Self{Ctx{f:[1,0,0,0,0,0,0,0,0,0,0 ,1,1,0,
0,0,0,0, 0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0, 0,0,0,0,
0,0,0,0, 1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,1,1,
0,0,0,0,0 ,0,0,0,0, 0,1,1,0,0
,0,0,0,0, 0,0,0,0, 1,1,0,0,0
,0,0,0,0, 0,0,0,1 ,1,0,0,0,
0,0,0 ,0,0,0 ,0,1,
1,0, 0,0,0,0 ,0,0
,0,0 ,0,1,1, 0,0,
0,0,0 ,0,0,0 ,0,0,
1,1,0, 0,0,0 ,0,0,
0,0,0, 0,1 ,1,0 ,0,0 ,0,0,0
,0,0,0 ,0,1 ,1,0, 0,0,0,0,0,0, 0,0, 0,1, 1,0,0,
0,0, 0,0, 0,0, 0,0,1,1,0,0,0 ,0, 0,0, 0,0,
0, 0,1, 1,0, 0,0,0,0,0,0,0, 0,0 ,1,1 ,0
,0 ,0 ,0,0 ,0,0,0,0,0,1,1 ,1, 1, 1
,1 ,1,1, 1,1,1,1,1,],bk :nb() ,p
:P:: new(),} }fn d(& mut
self) {let mut fb=self.f. clone
();for (p,v)in self.bk.iter ().
enumerate (){let px= p%BW;let py=p /BW;
let q=self.p.x +px+(self.p.y+ py
) *FW;if*v== 1&&q<FW*FH{fb[ q
] =1;}}println !("\x1b[H");for (
x, v)in fb.iter ().enumerate ()
{print !("{}",if* v==1{"#" }else
{"."} );if x%FW== FW-1{ println! ();}}
}fn collide(& self,np:&P) ->bool{for (p,v
)in self.bk.iter ().enumerate (){let px=
p% BW;let py= p/BW;let q=np .x+px+ (np
.y +py)*FW;if q>=FW*FH{ continue ;
}if *v&self.f [q]==1{return true ;}
}false }fn mv (&mut
self,np :P){if !self.
collide (&np){ self.p
=np;} self.d ()}fn
fix(& mut self ){for
(p,v )in self .bk.
iter (). enumerate
(){let px=p%BW; let
py=p/BW;if *v==1{self .f[self.p
.x+px+(self .p.y+py)*FW]=1;}}}fn el(&mut self){for y in 1..FH -1{let mut
can_erase =true;for p in y*FW..(y+1)*FW{if self.f[p]==0{ can_erase
=false; break ;}}if can_erase{for y2 in (2..=y). rev(){for
p in y2* FW..(y2+1)*FW{self.f[p]=self .f[p-FW]
;}}}}}fn sb(&mut self)->bool{self.p=P ::new()
;self.bk=nb() ;if self.collide(&self.p){self.d (); println!(
"Game Over!") ; println!("Press ESC to exit" ); return true;}
false}fn rr(& mut self){let mut nb:[usize;BS ]= Default::
default();for p in 0 ..BS{let px=p%BW;let py=p/ BW;let px2=py;let py2
=BW-1-px;nb[p ]= self.bk[px2+py2*BW];}if!self . collide(&self
.p){self.bk= nb;}self.d()}fn rl(& mut self){let
mut nb:[usize ;BS]= Default::default ();for p in 0..BS{let
px=p%BW;let py=p/BW ;let px2=py;let py2
=BW-1-px;nb[px2+py2 *BW]=self.bk[p];}if
!self.collide(&self .p){self.bk=nb;}self
.d()}}#[derive(Clone ,Copy )] struct P{x:usize,y
:usize,}impl P{fn new() ->Self {P{x:4,y:0}}}fn nb(
)->[usize;BS]{let mut rng =rand::thread_rng();let p
:usize=rng.gen_range(0..7 )*16 ;let mut r:[usize;BS]=[0;BS];for
i in 0..BS{r[i]=B[p+i];}r} const B :[ usize ;BS*7]=[0,0,0,0,0,0,0,0,1
,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0, 1,1 ,0, 0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0
,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0, 0,0, 0,0, 0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0
,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0 ,0,0,1 ,0,0,1,1,1,0,0,0,0,0,];fn quit(){println!
("\x1b[?25h");}
Windows11 + Rust 1.66.1 で動作確認。Windows のコマンドプロンプトで動作します。
[参考]
・Rustで作るテトリス風ゲーム入門
https://zenn.dev/kumavale/books/30efec2e1d3428/viewer/900eb4
エスケープシーケンスとキー入力の使い方など、非常に勉強になりました。
単なるコピペではなくフィールドやブロックのデータ構造を全く異なる形で実装しました。