Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Side by Side Diff: sky/examples/mine_digger/mine_digger.dart

Issue 1196213012: Add minedigger example (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 import 'dart:sky' as sky;
6 import 'dart:math';
7 import 'dart:core';
abarth-chromium 2015/06/23 04:05:17 You don't need to import dart:core. It's imported
8
9 import 'package:sky/rendering/flex.dart';
10 import 'package:sky/widgets/basic.dart';
11 import 'package:sky/painting/text_style.dart';
12
13 // Classic minesweeper-inspired game. The mouse controls are standard except
14 // for left + right combo which is not implemented. For touch, the duration of
15 // the pointer determines probing versus flagging.
16 //
17 // There are only 3 classes to understand. Game, which is contains all the
18 // logic and two UI classes: CoveredMineNode and ExposedMineNode, none of them
19 // holding state.
20
21 class Game {
22 static final int rows = 9;
23 static final int cols = 9;
24 static final int totalMineCount = 11;
25
26 static final int coveredCell = 0;
27 static final int explodedCell = 1;
28 static final int clearedCell = 2;
29 static final int flaggedCell = 3;
30 static final int shownCell = 4;
31
32 static final List<TextStyle> textStyles = new List<TextStyle>();
33
34 final App app;
35
36 bool alive;
37 bool hasWon;
38 int detectedCount;
39 int randomSeed;
40
41 // |cells| keeps track of the positions of the mines.
42 List<List<bool>> cells;
43 // |uiState| keeps track of the visible player progess.
44 List<List<int>> uiState;
45
46 Game(this.app) {
47 randomSeed = 22;
48 // Colors for each mine count:
49 // 0 - none, 1 - blue, 2-green, 3-red, 4-black, 5-dark red .. etc.
50 textStyles.add(
51 new TextStyle(color: const Color(0xFF555555), fontWeight: bold));
52 textStyles.add(
53 new TextStyle(color: const Color(0xFF0094FF), fontWeight: bold));
54 textStyles.add(
55 new TextStyle(color: const Color(0xFF13A023), fontWeight: bold));
56 textStyles.add(
57 new TextStyle(color: const Color(0xFFDA1414), fontWeight: bold));
58 textStyles.add(
59 new TextStyle(color: const Color(0xFF1E2347), fontWeight: bold));
60 textStyles.add(
61 new TextStyle(color: const Color(0xFF7F0037), fontWeight: bold));
62 textStyles.add(
63 new TextStyle(color: const Color(0xFFE93BE9), fontWeight: bold));
64 initialize();
65 }
66
67 void initialize() {
68 alive = true;
69 hasWon = false;
70 detectedCount = 0;
71 // Build the arrays.
72 cells = new List<List<bool>>();
73 uiState = new List<List<int>>();
74 for (int iy = 0; iy != rows; iy++) {
75 cells.add(new List<bool>());
76 uiState.add(new List<int>());
77 for (int ix = 0; ix != cols; ix++) {
78 cells[iy].add(false);
79 uiState[iy].add(coveredCell);
80 }
81 }
82 // Place the mines.
83 var random = new Random(++randomSeed);
84 for (int mc = 0; mc != totalMineCount; mc++) {
85 var rx = random.nextInt(rows);
86 var ry = random.nextInt(cols);
87 if (cells[ry][rx]) {
88 // Mine already there. Try again.
89 --mc;
90 } else {
91 cells[ry][rx] = true;
92 }
93 }
94 }
95
96 Widget generateBoard() {
97 bool hasCoveredCell = false;
98 var flex_rows = new List<Flex>();
abarth-chromium 2015/06/23 04:05:17 s/flex_rows/flexRows/
99 for (int iy = 0; iy != 9; iy++) {
100 var row = new List<Component>();
101 for (int ix = 0; ix != 9; ix++) {
102 int state = uiState[iy][ix];
103 int count = mineCount(ix, iy);
104
105 if (!alive) {
106 if (state != explodedCell)
107 state = cells[iy][ix] ? shownCell : state;
108 }
109
110 if (state == coveredCell) {
111 row.add(new CoveredMineNode(
112 this,
113 flagged: false,
114 posX: ix, posY: iy));
115 // Mutating |hasCoveredCell| here is hacky, but convenient, same
116 // goes for mutating |hasWon| below.
117 hasCoveredCell = true;
118 } else if (state == flaggedCell) {
119 row.add(new CoveredMineNode(
120 this,
121 flagged: true,
122 posX: ix, posY: iy));
123 }else {
abarth-chromium 2015/06/23 04:05:17 s/}else {/} else {/
124 row.add(new ExposedMineNode(
125 state: state,
126 count: count));
127 }
128 }
129 flex_rows.add(
130 new Flex(
131 row,
132 direction: FlexDirection.horizontal,
133 justifyContent: FlexJustifyContent.center,
134 key: 'flex_row($iy)'
135 ));
136 }
137
138 if (!hasCoveredCell) {
139 // all cells uncovered. Are all mines flagged?
140 if ((detectedCount == totalMineCount) && alive) {
141 hasWon = true;
142 print('game won!');
abarth-chromium 2015/06/23 04:05:17 Mobile first!
143 }
144 }
145
146 return new Container(
147 key: 'minefield',
148 padding: new EdgeDims.all(10.0),
149 margin: new EdgeDims.all(10.0),
150 decoration: new BoxDecoration(backgroundColor: const Color(0xFF6B6B6B)),
151 child: new Flex(
152 flex_rows,
153 direction: FlexDirection.vertical,
154 key: 'flxv'));
155 }
156
157 Widget generateUI() {
158 var board = generateBoard();
159 String banner = hasWon ?
160 'Awesome!!' : alive ?
161 'Mine Digger [$detectedCount-$totalMineCount]': 'Kaboom! [press here]';
162
163 return new Flex([
164 new Container(
165 padding: new EdgeDims.all(10.0),
166 margin: new EdgeDims.all(10.0),
167 decoration: new BoxDecoration(backgroundColor: const Color(0xFFC0C0C0)),
168 child: new Listener(
169 onPointerDown: handleBannerPointerDown,
170 child: new Text(banner))),
171 board,
172 new Container(
173 height: 100.0, width: 100.0,
174 decoration: new BoxDecoration(backgroundColor: const Color(0xFFCC1111))
175 )
176 ],
177 direction: FlexDirection.vertical,
178 justifyContent: FlexJustifyContent.spaceAround);
179 }
180
181 void handleBannerPointerDown(sky.PointerEvent event) {
182 initialize();
183 app.setState((){});
184 }
185
186 // User action. The user uncovers the cell which can cause losing the game.
187 void probe(int x, int y) {
188 if (!alive)
189 return;
190 if (uiState[y][x] == flaggedCell)
191 return;
192 // Allowed to probe.
193 if (cells[y][x]) {
194 print('kaboom!! $x $y');
195 uiState[y][x] = explodedCell;
196 alive = false;
197 } else {
198 // No mine, uncover nearby if possible.
199 cull(x, y);
200 }
201 app.setState((){});
202 }
203
204 // User action. The user is sure a mine is at this location.
205 void flag(int x, int y) {
206 if (uiState[y][x] == flaggedCell) {
207 uiState[y][x] = coveredCell;
208 --detectedCount;
209 } else {
210 uiState[y][x] = flaggedCell;
211 ++detectedCount;
212 }
213 app.setState((){});
214 }
215
216 // Recursively uncovers cells whose totalMineCount is zero.
217 void cull(int x, int y) {
218 if ((x < 0) || (x > rows - 1))
219 return;
220 if ((y < 0) || (y > cols - 1))
221 return;
222
223 if (uiState[y][x] == clearedCell)
224 return;
225 uiState[y][x] = clearedCell;
226
227 if (mineCount(x, y) > 0)
228 return;
229
230 cull(x - 1, y);
231 cull(x + 1, y);
232 cull(x, y - 1);
233 cull(x, y + 1 );
234 cull(x - 1, y - 1);
235 cull(x + 1, y + 1);
236 cull(x + 1, y - 1);
237 cull(x - 1, y + 1);
238 }
239
240 int mineCount(int x, int y) {
241 int count = 0;
242 int my = cols - 1;
243 int mx = rows - 1;
244
245 count += x > 0 ? bombs(x - 1, y) : 0;
246 count += x < mx ? bombs(x + 1, y) : 0;
247 count += y > 0 ? bombs(x, y - 1) : 0;
248 count += y < my ? bombs(x, y + 1 ) : 0;
249
250 count += (x > 0) && (y > 0) ? bombs(x - 1, y - 1) : 0;
251 count += (x < mx) && (y < my) ? bombs(x + 1, y + 1) : 0;
252 count += (x < mx) && (y > 0) ? bombs(x + 1, y - 1) : 0;
253 count += (x > 0) && (y < my) ? bombs(x - 1, y + 1) : 0;
254
255 return count;
256 }
257
258 int bombs(int x, int y) {
259 return cells[y][x] ? 1 : 0;
260 }
261 }
262
263 Widget makeCell(Widget widget) {
264 return new Container(
265 padding: new EdgeDims.all(1.0),
266 height: 27.0, width: 27.0,
267 decoration: new BoxDecoration(backgroundColor: const Color(0xFFC0C0C0)),
268 margin : new EdgeDims.all(2.0),
269 child : widget);
abarth-chromium 2015/06/23 04:05:17 s/margin :/margin:/ s/child :/child:/
270 }
271
272 Widget makeInnerCell(Widget widget) {
273 return new Container(
274 padding: new EdgeDims.all(1.0),
275 margin: new EdgeDims.all(3.0),
276 height: 17.0, width: 17.0,
277 child: widget);
278 }
279
280 class CoveredMineNode extends Component {
281 final Game game;
282 final bool flagged;
283 final int posX;
284 final int posY;
285 Stopwatch stopwatch;
286
287 CoveredMineNode(this.game, {this.flagged, this.posX, this.posY});
288
289 void _handlePointerDown(sky.PointerEvent event) {
290 if (event.buttons == 1) {
291 game.probe(posX, posY);
292 } else if (event.buttons == 2) {
293 game.flag(posX, posY);
294 } else {
295 // Touch event.
296 stopwatch = new Stopwatch()..start();
297 }
298 }
299
300 void _handlePointerUp(sky.PointerEvent event) {
301 if (stopwatch == null)
302 return;
303 // Pointer down was a touch event.
304 var ms = stopwatch.elapsedMilliseconds;
305 if (stopwatch.elapsedMilliseconds < 250) {
306 game.probe(posX, posY);
307 } else {
308 // Long press flags.
309 game.flag(posX, posY);
310 }
311 stopwatch = null;
312 }
313
314 Widget build() {
315 var text = flagged ?
316 makeInnerCell(new StyledText(elements : [Game.textStyles[5], '\u2691'])) :
317 null;
318
319 var inner = new Container(
320 margin: new EdgeDims.all(2.0),
321 height: 17.0, width: 17.0,
322 decoration: new BoxDecoration(backgroundColor: const Color(0xFFD9D9D9)),
323 child : text);
abarth-chromium 2015/06/23 04:05:17 s/child :/child:/
324
325 return makeCell(new Listener(
326 child: inner,
327 onPointerDown: _handlePointerDown,
328 onPointerUp: _handlePointerUp));
329 }
330 }
331
332 class ExposedMineNode extends Component {
333 final int state;
334 final int count;
335
336 ExposedMineNode({this.state, this.count});
337
338 Widget build() {
339 StyledText text;
340 if (state == Game.clearedCell) {
341 // Uncovered cell with nearby mine count.
342 if (count != 0)
343 text = new StyledText(elements : [Game.textStyles[count], '$count']);
344 } else {
345 // Exploded mine or shown mine for 'game over'.
346 var color = state == Game.explodedCell ? 3 : 0;
347 text = new StyledText(elements : [Game.textStyles[color], '\u2600']);
348 }
349
350 return makeCell(makeInnerCell(text));
351 }
352 }
353
354 class MineDiggerApp extends App {
355 Game game;
356
357 MineDiggerApp() {
358 game = new Game(this);
359 }
360
361 Widget build() {
362 return game.generateUI();
363 }
364 }
365
366 void main() {
367 runApp(new MineDiggerApp());
368 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698