1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! UI implementations for event handling
use crossterm::{
event::{self, Event, KeyEvent, KeyCode, KeyModifiers},
terminal::{self, Clear, ClearType},
ExecutableCommand
};
use crate::{
gameboard::{BoardSpaceLocation, BoardSpace},
active_player::ActivePlayer
};
use std::io::stdout;
impl super::UI {
/// Move cursor to the right (positive x) if possible
///
/// Returns `true` if successful, `false` if not
pub(super) fn move_cursor_right(&mut self) -> bool
{
if self.cursor_x_pos < 2{
self.cursor_x_pos += 1;
true
} else {
false
}
}
/// Move cursor to the left (negative x) if possible
///
/// Returns `true` if successful, `false` if not
pub(super) fn move_cursor_left(&mut self) -> bool
{
if self.cursor_x_pos > 0{
self.cursor_x_pos -= 1;
true
} else {
false
}
}
/// Move cursor downwards (positive y) if possible
///
/// Returns `true` if successful, `false` if not
pub(super) fn move_cursor_down(&mut self) -> bool
{
if self.cursor_y_pos < 2{
self.cursor_y_pos += 1;
true
} else {
false
}
}
/// Move cursor upwards (negative y) if possible
///
/// Returns `true` if successful, `false` if not
pub(super) fn move_cursor_up(&mut self) -> bool
{
if self.cursor_y_pos > 0{
self.cursor_y_pos -= 1;
true
} else {
false
}
}
/// Claim the selected space for the active player if possible
///
/// Returns `true` if successful, `false` if not
pub(super) fn claim_space(&mut self) -> bool
{
let desired_location =
BoardSpaceLocation::from_coordinates((self.cursor_x_pos, self.cursor_y_pos));
let desired_space =
self.game_board.space_mut(desired_location);
// only update space and switch players if selected space is empty
if desired_space == &BoardSpace::Empty {
//write active player letter to this space
*desired_space = match self.active_player {
ActivePlayer::PlayerX => BoardSpace::X,
ActivePlayer::PlayerO => BoardSpace::O
};
true
} else {
false
}
}
/// Switches the active player and resets cursor position
pub(super) fn switch_active_player(&mut self)
{
//switch player
self.active_player.switch();
//reset cursor position
self.reset_cursor_pos();
}
/// Blocks until a [crossterm::event::Event] is available, then handles it
pub(super) fn handle_next_event(&mut self) -> crossterm::Result<()>
{
match event::read()? {
Event::Key(key_event) => {
match key_event {
KeyEvent{code:KeyCode::Right, ..} => {
self.move_cursor_right();
},
KeyEvent{code:KeyCode::Left, ..} => {
self.move_cursor_left();
},
KeyEvent{code:KeyCode::Down, ..} => {
self.move_cursor_down();
},
KeyEvent{code:KeyCode::Up, ..} => {
self.move_cursor_up();
},
KeyEvent{code:KeyCode::Enter, ..} => {
//attempt to claim space and switch turns if successful
if self.claim_space(){
self.switch_active_player();
}
},
KeyEvent{code:KeyCode::Char('x'), ..} => {
//attempt to claim space if active player is X
if self.active_player == ActivePlayer::PlayerX && self.claim_space(){
self.switch_active_player();
}
},
KeyEvent{code:KeyCode::Char('o'), ..} => {
//attempt to claim space if active player is O
if self.active_player == ActivePlayer::PlayerO && self.claim_space(){
self.switch_active_player();
}
},
KeyEvent{code:KeyCode::Char('q'), ..} => {
self.exit_flag = true;
},
KeyEvent{code:KeyCode::Char('c'), modifiers:KeyModifiers::CONTROL, ..} => {
self.exit_flag = true;
}
_ => {
//ignore other KeyEvents
}
}
},
//ignore size returned by resize event as it is currently (as of crossterm 0.25) wrong on Windows
Event::Resize(_,_) => {
let (new_x, new_y) = terminal::size()?;
self.terminal_x_size = new_x;
self.terminal_y_size = new_y;
stdout().execute(Clear(ClearType::All))?;
}
_ => {
//ignore other Events
}
}
Ok(())
}
}