initial
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "term_2048"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
crossterm = "0.27.0"
|
||||
rand = "0.8.5"
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
RUSTFLAGS="-Zlocation-detail=none" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --release
|
||||
upx --best --lzma target/release/term_2048
|
||||
cp target/release/term_2048 term_2048
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
use crossterm::{
|
||||
event::{poll, read, Event, KeyCode},
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use std::{io, io::Write};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Number(u32),
|
||||
}
|
||||
|
||||
struct Board([[Cell; 4]; 4]);
|
||||
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Right,
|
||||
Left,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut board = Board::new();
|
||||
let mut score = 0;
|
||||
print!("\x1b[?25l");
|
||||
loop {
|
||||
board.spawn_cell().unwrap();
|
||||
board.render(&score);
|
||||
let copy = board.0.clone();
|
||||
while copy == board.0 {
|
||||
io::stdout().flush().unwrap();
|
||||
enable_raw_mode().unwrap();
|
||||
let direction = get_input().unwrap();
|
||||
disable_raw_mode().unwrap();
|
||||
board.move_cells(&direction);
|
||||
board.merge_cells(&direction, &mut score);
|
||||
board.move_cells(&direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_input() -> Option<Direction> {
|
||||
loop {
|
||||
while poll(std::time::Duration::from_millis(16)).ok()? {
|
||||
if let Event::Key(k) = read().ok()? {
|
||||
if let KeyCode::Char(c) = k.code {
|
||||
match c {
|
||||
'h' => return Some(Direction::Left),
|
||||
'l' => return Some(Direction::Right),
|
||||
'k' => return Some(Direction::Up),
|
||||
'j' => return Some(Direction::Down),
|
||||
'q' => exit(),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exit() -> ! {
|
||||
disable_raw_mode().unwrap();
|
||||
print!("\x1b[?25h\x1b[2J\x1b[H");
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
impl Board {
|
||||
fn new() -> Self {
|
||||
Self([[Cell::Empty; 4]; 4])
|
||||
}
|
||||
|
||||
fn render(&self, score: &u32) {
|
||||
let mut s = String::new();
|
||||
s.push_str("\x1b[1m\x1b[32mscore\x1b[m: ");
|
||||
s.push_str(&score.to_string());
|
||||
s.push('\n');
|
||||
s.push('┏');
|
||||
for _ in 0..3 {
|
||||
s.push_str(&"━".repeat(4));
|
||||
s.push('┳');
|
||||
}
|
||||
s.push_str(&"━".repeat(4));
|
||||
s.push('┓');
|
||||
s.push('\n');
|
||||
for (i, line) in self.0.iter().enumerate() {
|
||||
s.push_str("┃");
|
||||
for cell in line {
|
||||
match cell {
|
||||
Cell::Empty => s.push_str(" "),
|
||||
Cell::Number(n) => {
|
||||
let n_s = n.to_string();
|
||||
let len = n_s.len();
|
||||
s.push_str(&(n_s + &" ".repeat(4 - len)))
|
||||
}
|
||||
}
|
||||
s.push_str("┃");
|
||||
}
|
||||
if i < self.0.len() - 1 {
|
||||
s.push('\n');
|
||||
s.push('┣');
|
||||
for _ in 0..3 {
|
||||
s.push_str("━━━━");
|
||||
s.push('╋');
|
||||
}
|
||||
s.push_str("━━━━");
|
||||
s.push('┫');
|
||||
s.push('\n');
|
||||
} else {
|
||||
s.push('\n');
|
||||
s.push('┗');
|
||||
for _ in 0..3 {
|
||||
s.push_str("━━━━");
|
||||
s.push('┻');
|
||||
}
|
||||
s.push_str("━━━━");
|
||||
s.push('┛');
|
||||
s.push('\n');
|
||||
}
|
||||
}
|
||||
print!("\x1b[2J\x1b[H{}", s)
|
||||
}
|
||||
|
||||
fn spawn_cell(&mut self) -> Option<()> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let r = rng.gen_range(0..3);
|
||||
let n = if r == 2 { 4 } else { 2 };
|
||||
let mut empty_count = 0;
|
||||
self.0.iter().for_each(|m| {
|
||||
m.iter().for_each(|c| {
|
||||
if let Cell::Empty = c {
|
||||
empty_count += 1;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if empty_count == 0 {
|
||||
return None;
|
||||
}
|
||||
let spawn = rng.gen_range(0..empty_count);
|
||||
|
||||
let mut index = 0;
|
||||
let mut pos = None;
|
||||
self.0.iter().enumerate().for_each(|(i, m)| {
|
||||
m.iter().enumerate().for_each(|(j, c)| {
|
||||
if let Cell::Empty = c {
|
||||
if index == spawn {
|
||||
pos = Some((j, i));
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if let Some((x, y)) = pos {
|
||||
self.0[y][x] = Cell::Number(n);
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn move_cells(&mut self, direction: &Direction) {
|
||||
let dyn_board: Vec<Vec<u32>> = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|m| {
|
||||
m.iter()
|
||||
.filter_map(|c| {
|
||||
if let Cell::Number(n) = c {
|
||||
Some(*n)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
match direction {
|
||||
Direction::Up => {
|
||||
let copy = self.0.clone();
|
||||
self.0 = [[Cell::Empty; 4]; 4];
|
||||
let mut index = 0;
|
||||
for i in (0..4).rev() {
|
||||
for j in 0..4 {
|
||||
if let Cell::Number(n) = copy[j][i] {
|
||||
self.0[index][i] = Cell::Number(n);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
Direction::Down => {
|
||||
let copy = self.0.clone();
|
||||
self.0 = [[Cell::Empty; 4]; 4];
|
||||
let mut index = 0;
|
||||
for i in 0..4 {
|
||||
for j in 0..4 {
|
||||
if let Cell::Number(n) = copy[3 - j][i] {
|
||||
self.0[3 - index][i] = Cell::Number(n);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
Direction::Right => {
|
||||
self.0 = [[Cell::Empty; 4]; 4];
|
||||
self.0.iter_mut().enumerate().for_each(|(i, m)| {
|
||||
dyn_board[i].iter().rev().enumerate().for_each(|(j, n)| {
|
||||
m[3 - j] = Cell::Number(*n);
|
||||
})
|
||||
});
|
||||
}
|
||||
Direction::Left => {
|
||||
self.0 = [[Cell::Empty; 4]; 4];
|
||||
self.0.iter_mut().enumerate().for_each(|(i, m)| {
|
||||
dyn_board[i].iter().enumerate().for_each(|(j, n)| {
|
||||
m[j] = Cell::Number(*n);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_cells(&mut self, direction: &Direction, score: &mut u32) {
|
||||
match direction {
|
||||
Direction::Up => {
|
||||
for i in 0..4 {
|
||||
for j in 0..3 {
|
||||
if let Cell::Number(a) = self.0[j][i] {
|
||||
if let Cell::Number(b) = self.0[j + 1][i] {
|
||||
if a == b {
|
||||
self.0[j][i] = Cell::Number(b * 2);
|
||||
self.0[j + 1][i] = Cell::Empty;
|
||||
*score += b * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::Down => {
|
||||
for i in 0..4 {
|
||||
for j in (0..3).rev() {
|
||||
if let Cell::Number(a) = self.0[j][i] {
|
||||
if let Cell::Number(b) = self.0[j + 1][i] {
|
||||
if a == b {
|
||||
self.0[j + 1][i] = Cell::Number(b * 2);
|
||||
self.0[j][i] = Cell::Empty;
|
||||
*score += b * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::Right => {
|
||||
for line in self.0.iter_mut() {
|
||||
for i in (0..3).rev() {
|
||||
if let Cell::Number(a) = line[i] {
|
||||
if let Cell::Number(b) = line[i + 1] {
|
||||
if a == b {
|
||||
line[i + 1] = Cell::Number(b * 2);
|
||||
line[i] = Cell::Empty;
|
||||
*score += b * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::Left => {
|
||||
for line in self.0.iter_mut() {
|
||||
for i in (1..4).rev() {
|
||||
if let Cell::Number(a) = line[4 - i] {
|
||||
if let Cell::Number(b) = line[3 - i] {
|
||||
if a == b {
|
||||
line[3 - i] = Cell::Number(b * 2);
|
||||
line[4 - i] = Cell::Empty;
|
||||
*score += b * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user