jag

2048_rust

public

Small 2048 game in Rust

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