jag

2048_rust

public

Small 2048 game in Rust

Star0
Watch0
7e8970ba55fa8eeea2bcfade7c122af806ae2815
1
ssh://git@git.jaglekon.de:30/jag/2048_rust.git
src/main.rsRust230 lines5.4 KB
1
use std::io::{self, Write};
2
use std::io::{Read, stdin};
3
use std::os::unix::io::AsRawFd;
4
use termios::*;
5
6
#[derive(Debug, Clone)]
7
pub struct TermMode {
8
    original: Termios,
9
    fd: i32,
10
}
11
12
impl TermMode {
13
    pub fn new() -> Self {
14
        let fd = stdin().as_raw_fd();
15
16
        let original = Termios::from_fd(fd).unwrap();
17
18
        let mut new = original.clone();
19
        new.c_lflag &= !(ECHO | ICANON);
20
        new.c_cc[VMIN] = 1;
21
        new.c_cc[VTIME] = 0;
22
23
        tcsetattr(fd, TCSANOW, &new).unwrap();
24
25
        Self { original, fd }
26
    }
27
}
28
29
impl Drop for TermMode {
30
    fn drop(&mut self) {
31
        tcsetattr(self.fd, TCSANOW, &self.original).unwrap();
32
    }
33
}
34
35
#[derive(Debug, PartialEq)]
36
enum Direction {
37
    Up,
38
    Down,
39
    Right,
40
    Left,
41
    None,
42
}
43
44
fn print_field(field: [[u16; 4]; 4]) {
45
    print!("┏━━━━┯━━━━┯━━━━┯━━━━┓\n");
46
47
    for i in 0..4 {
48
        print!("┃");
49
50
        for j in 0..4 {
51
            match field[i][j] {
52
                0 => print!("    "),
53
                n => print!("{:4}", n),
54
            };
55
56
            print!("{}", if j == 3 { "┃\n" } else { "│" });
57
        }
58
        //print!("\x1b[D┃\n┃    │    │    │    ┃\n");
59
60
        if i != 3 {
61
            print!("┠────┼────┼────┼────┨\n");
62
        }
63
    }
64
65
    print!("┗━━━━┷━━━━┷━━━━┷━━━━┛\n");
66
67
    io::stdout().flush().unwrap();
68
}
69
70
fn move_field(direction: Direction, field: [[u16; 4]; 4]) -> [[u16; 4]; 4] {
71
    if direction == Direction::None {
72
        return field;
73
    }
74
75
    let mut new_field = field.clone();
76
77
    for i in 0..4 {
78
        let mut lock = false;
79
        for j in 1..4 {
80
            for k in (1..=j).rev() {
81
                let this = match direction {
82
                    Direction::Up => (k, i),
83
                    Direction::Down => (4 - k - 1, i),
84
                    Direction::Right => (i, 4 - k - 1),
85
                    Direction::Left => (i, k),
86
                    _ => (i, k),
87
                };
88
                let next = match direction {
89
                    Direction::Up => (k - 1, i),
90
                    Direction::Down => (4 - k, i),
91
                    Direction::Right => (i, 4 - k),
92
                    Direction::Left => (i, k - 1),
93
                    _ => (i, k),
94
                };
95
96
                if new_field[this.0][this.1] == 0 {
97
                    break;
98
                }
99
100
                if new_field[this.0][this.1] == new_field[next.0][next.1] {
101
                    if lock == true {
102
                        lock = false;
103
                        break;
104
                    }
105
                    new_field[this.0][this.1] = 0;
106
                    new_field[next.0][next.1] *= 2;
107
                    lock = true;
108
                    break;
109
                } else if new_field[next.0][next.1] == 0 {
110
                    new_field[next.0][next.1] = new_field[this.0][this.1];
111
                    new_field[this.0][this.1] = 0;
112
                }
113
            }
114
        }
115
    }
116
    new_field
117
}
118
119
fn make_rand_num(field: &mut [[u16; 4]; 4]) {
120
    let what_value = rand::random_range(0..10);
121
122
    let mut coord = rand::random_range(0..16);
123
124
    while field[coord / 4][coord % 4] != 0 {
125
        coord = rand::random_range(0..16);
126
    }
127
128
    field[coord / 4][coord % 4] = match what_value {
129
        0 => 4,
130
        _ => 2,
131
    }
132
}
133
134
fn field_has_empty(field: [[u16; 4]; 4]) -> bool {
135
    for i in 0..4 {
136
        for j in 0..4 {
137
            if field[i][j] == 0 {
138
                return true;
139
            }
140
        }
141
    }
142
    false
143
}
144
145
fn has_lost(field: [[u16; 4]; 4]) -> bool {
146
    if field_has_empty(field) {
147
        return false;
148
    }
149
150
    for i in 0..4 {
151
        for j in 0..4 {
152
            if i > 0 && field[i][j] == field[i - 1][j] {
153
                return false;
154
            }
155
156
            if j > 0 && field[i][j] == field[i][j - 1] {
157
                return false;
158
            }
159
160
            if i < 3 && field[i][j] == field[i + 1][j] {
161
                return false;
162
            }
163
164
            if j < 3 && field[i][j] == field[i][j + 1] {
165
                return false;
166
            }
167
        }
168
    }
169
170
    true
171
}
172
173
fn get_direction(input: u8) -> Option<Direction> {
174
    match input {
175
        0x41 => Some(Direction::Up),
176
        0x42 => Some(Direction::Down),
177
        0x43 => Some(Direction::Right),
178
        0x44 => Some(Direction::Left),
179
        _ => None,
180
    }
181
}
182
183
fn main() {
184
    let _term = TermMode::new();
185
186
    let mut field: [[u16; 4]; 4] = [[0; 4]; 4];
187
188
    make_rand_num(&mut field);
189
    make_rand_num(&mut field);
190
191
    let mut stdin = stdin();
192
    let mut buf = [0u8; 10];
193
194
    print!("\x1b[?25l\x1b[?1049h\x1b[2J\x1b[H");
195
    io::stdout().flush().unwrap();
196
197
    loop {
198
        print!("\x1b[1;1H");
199
        print_field(field);
200
201
        let n = stdin.read(&mut buf).unwrap();
202
203
        if n > 0 && (buf[0] == b'q' || buf[0] == b'Q') {
204
            break;
205
        }
206
207
        if n == 3 && buf[0] == 0x1b && buf[1] == 0x5b {
208
            let tmp = match get_direction(buf[2]) {
209
                Some(direction) => move_field(direction, field),
210
                None => continue,
211
            };
212
213
            if tmp != field {
214
                field = tmp;
215
216
                if field_has_empty(field) {
217
                    make_rand_num(&mut field);
218
                }
219
            }
220
221
            if has_lost(field) {
222
                break;
223
            }
224
        }
225
    }
226
227
    print!("\x1b[?1049l\x1b[?25h");
228
    print_field(field);
229
    io::stdout().flush().unwrap();
230
}