jag

2048_rust

public

Small 2048 game in Rust

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