1
+ #include " Snake.h"
2
+
3
+ #include " Utils.h"
4
+ #include < conio.h>
5
+
6
+ #define H_FIELD 25
7
+ #define W_FIELD 50
8
+
9
+ #define FRUIT ' @'
10
+ #define BODY ' '
11
+ #define H_EDGE ' ='
12
+ #define V_EDGE ' |'
13
+
14
+ #include " Utils.h"
15
+
16
+ Snake::Snake () {
17
+
18
+ initSnake ();
19
+ }
20
+
21
+ void Snake::initSnake () {
22
+
23
+ Utils::showConsoleCursor (false );
24
+
25
+ if (!Utils::genRandomInt (0 , 1 )) {
26
+
27
+ m_vx = V_NUL;
28
+ m_vy = Utils::genRandomInt (0 , 1 ) ? V_POS : V_NEG;
29
+ }
30
+ else {
31
+
32
+ m_vy = V_NUL;
33
+ m_vx = Utils::genRandomInt (0 , 1 ) ? V_POS : V_NEG;
34
+ }
35
+
36
+ m_fruit = {};
37
+ m_countFruit = {};
38
+
39
+ m_body.clear ();
40
+
41
+ // Vector of field, borders are excluded
42
+ for (int y = 1 ; y < H_FIELD - 1 ; y++) {
43
+
44
+ for (int x = 1 ; x < W_FIELD - 1 ; x++)
45
+ m_field.push_back ({x, y});
46
+ }
47
+
48
+ genBody ();
49
+ genFruit ();
50
+ }
51
+
52
+ void Snake::run () {
53
+
54
+ while (isRunning ()) {
55
+
56
+ readUserInput ();
57
+ genFruit ();
58
+ updateBodyCoord ();
59
+ drawField ();
60
+
61
+ // if (m_deltaT)
62
+ // Sleep(m_deltaT);
63
+ }
64
+ }
65
+
66
+ bool Snake::isRunning () {
67
+ return m_run;
68
+ }
69
+
70
+ void Snake::readUserInput () {
71
+
72
+ if (_kbhit ()) {
73
+
74
+ switch (_getch ()) {
75
+
76
+ case KEY_W:
77
+
78
+ setYDirection (V_NEG);
79
+ setXDirection (V_NUL);
80
+ break ;
81
+
82
+ case KEY_S:
83
+
84
+ setYDirection (V_POS);
85
+ setXDirection (V_NUL);
86
+ break ;
87
+
88
+
89
+ case KEY_A:
90
+
91
+ setXDirection (V_NEG);
92
+ setYDirection (V_NUL);
93
+ break ;
94
+
95
+ case KEY_D:
96
+
97
+ setXDirection (V_POS);
98
+ setYDirection (V_NUL);
99
+ break ;
100
+ }
101
+ }
102
+ }
103
+
104
+ void Snake::setXDirection (V vel) {
105
+
106
+ m_vx = vel;
107
+ }
108
+
109
+ void Snake::setYDirection (V vel) {
110
+
111
+ m_vy = vel;
112
+ }
113
+
114
+ void Snake::drawField () {
115
+
116
+ system (" cls" );
117
+
118
+ for (int y = 0 ; y < H_FIELD; y++) {
119
+
120
+ for (int x = 0 ; x < W_FIELD; x++) {
121
+
122
+ if (y == 0 || y == H_FIELD - 1 )
123
+ Utils::drawElement (H_EDGE);
124
+ else {
125
+
126
+ if (x == 0 || x == W_FIELD - 1 )
127
+ Utils::drawElement (V_EDGE);
128
+ else {
129
+
130
+ // Here i am in the field
131
+
132
+ // Spawn or eat fruit
133
+ if (checkFruit (x, y)) {
134
+
135
+ // Spawn
136
+ if (!checkHead (x, y)) {
137
+
138
+ if (m_fruit)
139
+ Utils::drawElement (FRUIT, RED_TXT);
140
+ else
141
+ Utils::drawElement (' ' );
142
+ }
143
+ // Eat
144
+ else {
145
+
146
+ // Generate new fruit coordinates and spawn at next iteration
147
+ m_fruit = false ;
148
+ m_countFruit++;
149
+
150
+ // Increase speed
151
+ // if (m_deltaT)
152
+ // m_deltaT - m_countFruit * 5;
153
+
154
+ // Draw head
155
+ Utils::drawElement (BODY, BACKGROUND_GREEN);
156
+ }
157
+
158
+ }
159
+ // Color actual snake positions
160
+ else if (checkBody (x, y)) {
161
+
162
+ if (checkHead (x, y))
163
+ Utils::drawElement (BODY, m_run ? BACKGROUND_GREEN : BACKGROUND_RED);
164
+ else
165
+ Utils::drawElement (BODY, HIGHLIGHT_TXT);
166
+ }
167
+ else
168
+ Utils::drawElement (' ' );
169
+ }
170
+ }
171
+ if (x == W_FIELD - 1 )
172
+ std::cout << " " << std::endl;
173
+ }
174
+ }
175
+
176
+ std::cout << " " << std::endl;
177
+
178
+ // Lose condition
179
+ if (!m_run) {
180
+
181
+ Utils::showConsoleCursor (true );
182
+ std::cout << " Ouch!" << " \n " ;
183
+
184
+ std::string choice = " " ;
185
+ while (choice != " y" && choice != " n" ) {
186
+
187
+ std::cout << " Do you wanna play again (y/n)? " ;
188
+ std::cin >> choice;
189
+
190
+ if (choice == " y" ) {
191
+
192
+ m_run = true ;
193
+ initSnake ();
194
+ }
195
+ else if (choice == " n" )
196
+ m_run = false ;
197
+ }
198
+ }
199
+ }
200
+
201
+ void Snake::checkEdges () {
202
+
203
+ if (m_y < 1 )
204
+ m_y = H_FIELD - 2 ;
205
+ else if (m_y > H_FIELD - 2 )
206
+ m_y = 1 ;
207
+
208
+ if (m_x < 1 )
209
+ m_x = W_FIELD - 2 ;
210
+ else if (m_x > W_FIELD - 2 )
211
+ m_x = 1 ;
212
+ }
213
+
214
+ bool Snake::checkHead (int x, int y) {
215
+ return (x == m_x && y == m_y);
216
+ }
217
+
218
+ bool Snake::checkFruit (int x, int y) {
219
+ return (x == m_xFruit && y == m_yFruit);
220
+ }
221
+
222
+ bool Snake::checkBody (int x, int y) {
223
+
224
+ std::pair<int , int > actualCoord (x, y);
225
+ if (std::find (m_body.begin (), m_body.end (), actualCoord) != m_body.end ())
226
+ return true ;
227
+ else
228
+ return false ;
229
+ }
230
+
231
+ void Snake::updateBodyCoord () {
232
+
233
+ m_x = m_x + m_vx * m_xT;
234
+ m_y = m_y + m_vy * m_yT;
235
+
236
+ checkEdges ();
237
+
238
+ std::pair<int , int > actualCoord (m_x, m_y);
239
+
240
+ if (find (m_body.begin (), m_body.end (), actualCoord) != m_body.end ())
241
+ m_run = false ;
242
+
243
+ m_body.push_back (actualCoord);
244
+
245
+ /* I want to keep m_countFruit + 1 coordinates (head)
246
+ Add the new head coordinate and remove tail coordinate
247
+ */
248
+ while (m_body.size () > (m_countFruit + 1 ))
249
+ m_body.erase (m_body.begin ());
250
+ }
251
+
252
+ void Snake::genBody () {
253
+
254
+ int randomIndex = Utils::genRandomInt (0 , m_field.size () - 1 );
255
+ m_x = m_field.at (randomIndex).first ;
256
+ m_y = m_field.at (randomIndex).second ;
257
+
258
+ m_body.push_back (std::pair<int , int >{m_x, m_y});
259
+ }
260
+
261
+ void Snake::genFruit () {
262
+
263
+ if (!m_fruit) {
264
+
265
+ // Need to do m_field - m_body and pick a random pair from the result
266
+ std::sort (m_field.begin (), m_field.end ());
267
+
268
+ // i do not want the m_body to be sorted
269
+ std::vector<std::pair<int , int >> m_bodyCopy = m_body;
270
+ std::sort (m_bodyCopy.begin (), m_bodyCopy.end ());
271
+
272
+ std::vector<std::pair<int , int >> availableFruitPos;
273
+ std::set_difference (
274
+ m_field.begin (), m_field.end (), m_bodyCopy.begin (), m_bodyCopy.end (),
275
+ std::back_inserter ( availableFruitPos )
276
+ );
277
+
278
+ int randomIndex = Utils::genRandomInt (0 , availableFruitPos.size () - 1 );
279
+ m_xFruit = availableFruitPos.at (randomIndex).first ;
280
+ m_yFruit = availableFruitPos.at (randomIndex).second ;
281
+
282
+ m_fruit = true ;
283
+ }
284
+ }
0 commit comments