| Index: sky/examples/mine_digger/mine_digger.dart
|
| diff --git a/sky/examples/mine_digger/mine_digger.dart b/sky/examples/mine_digger/mine_digger.dart
|
| deleted file mode 100644
|
| index aa205638cdbf2a393ea46e7ae3ec68ea4b5702c9..0000000000000000000000000000000000000000
|
| --- a/sky/examples/mine_digger/mine_digger.dart
|
| +++ /dev/null
|
| @@ -1,373 +0,0 @@
|
| -// Copyright 2015 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -import 'dart:sky' as sky;
|
| -import 'dart:math';
|
| -
|
| -import 'package:sky/rendering/flex.dart';
|
| -import 'package:sky/widgets/basic.dart';
|
| -import 'package:sky/widgets/scaffold.dart';
|
| -import 'package:sky/widgets/tool_bar.dart';
|
| -import 'package:sky/widgets/theme.dart';
|
| -import 'package:sky/theme/colors.dart' as colors;
|
| -import 'package:sky/painting/text_style.dart';
|
| -
|
| -// Classic minesweeper-inspired game. The mouse controls are standard except
|
| -// for left + right combo which is not implemented. For touch, the duration of
|
| -// the pointer determines probing versus flagging.
|
| -//
|
| -// There are only 3 classes to understand. Game, which is contains all the
|
| -// logic and two UI classes: CoveredMineNode and ExposedMineNode, none of them
|
| -// holding state.
|
| -
|
| -class Game {
|
| - static const int rows = 9;
|
| - static const int cols = 9;
|
| - static const int totalMineCount = 11;
|
| -
|
| - static const int coveredCell = 0;
|
| - static const int explodedCell = 1;
|
| - static const int clearedCell = 2;
|
| - static const int flaggedCell = 3;
|
| - static const int shownCell = 4;
|
| -
|
| - static final List<TextStyle> textStyles = new List<TextStyle>();
|
| -
|
| - final App app;
|
| -
|
| - bool alive;
|
| - bool hasWon;
|
| - int detectedCount;
|
| - int randomSeed;
|
| -
|
| - // |cells| keeps track of the positions of the mines.
|
| - List<List<bool>> cells;
|
| - // |uiState| keeps track of the visible player progess.
|
| - List<List<int>> uiState;
|
| -
|
| - Game(this.app) {
|
| - randomSeed = 22;
|
| - // Colors for each mine count:
|
| - // 0 - none, 1 - blue, 2-green, 3-red, 4-black, 5-dark red .. etc.
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFF555555), fontWeight: bold));
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFF0094FF), fontWeight: bold));
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFF13A023), fontWeight: bold));
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFFDA1414), fontWeight: bold));
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFF1E2347), fontWeight: bold));
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFF7F0037), fontWeight: bold));
|
| - textStyles.add(
|
| - new TextStyle(color: const Color(0xFFE93BE9), fontWeight: bold));
|
| - initialize();
|
| - }
|
| -
|
| - void initialize() {
|
| - alive = true;
|
| - hasWon = false;
|
| - detectedCount = 0;
|
| - // Build the arrays.
|
| - cells = new List<List<bool>>();
|
| - uiState = new List<List<int>>();
|
| - for (int iy = 0; iy != rows; iy++) {
|
| - cells.add(new List<bool>());
|
| - uiState.add(new List<int>());
|
| - for (int ix = 0; ix != cols; ix++) {
|
| - cells[iy].add(false);
|
| - uiState[iy].add(coveredCell);
|
| - }
|
| - }
|
| - // Place the mines.
|
| - Random random = new Random(++randomSeed);
|
| - for (int mc = 0; mc != totalMineCount; mc++) {
|
| - int rx = random.nextInt(rows);
|
| - int ry = random.nextInt(cols);
|
| - if (cells[ry][rx]) {
|
| - // Mine already there. Try again.
|
| - --mc;
|
| - } else {
|
| - cells[ry][rx] = true;
|
| - }
|
| - }
|
| - }
|
| -
|
| - Widget buildBoard() {
|
| - bool hasCoveredCell = false;
|
| - List<Flex> flexRows = new List<Flex>();
|
| - for (int iy = 0; iy != 9; iy++) {
|
| - List<Component> row = new List<Component>();
|
| - for (int ix = 0; ix != 9; ix++) {
|
| - int state = uiState[iy][ix];
|
| - int count = mineCount(ix, iy);
|
| -
|
| - if (!alive) {
|
| - if (state != explodedCell)
|
| - state = cells[iy][ix] ? shownCell : state;
|
| - }
|
| -
|
| - if (state == coveredCell) {
|
| - row.add(new CoveredMineNode(
|
| - this,
|
| - flagged: false,
|
| - posX: ix, posY: iy));
|
| - // Mutating |hasCoveredCell| here is hacky, but convenient, same
|
| - // goes for mutating |hasWon| below.
|
| - hasCoveredCell = true;
|
| - } else if (state == flaggedCell) {
|
| - row.add(new CoveredMineNode(
|
| - this,
|
| - flagged: true,
|
| - posX: ix, posY: iy));
|
| - } else {
|
| - row.add(new ExposedMineNode(
|
| - state: state,
|
| - count: count));
|
| - }
|
| - }
|
| - flexRows.add(
|
| - new Flex(
|
| - row,
|
| - direction: FlexDirection.horizontal,
|
| - justifyContent: FlexJustifyContent.center,
|
| - key: 'flex_row($iy)'
|
| - ));
|
| - }
|
| -
|
| - if (!hasCoveredCell) {
|
| - // all cells uncovered. Are all mines flagged?
|
| - if ((detectedCount == totalMineCount) && alive) {
|
| - hasWon = true;
|
| - }
|
| - }
|
| -
|
| - return new Container(
|
| - key: 'minefield',
|
| - padding: new EdgeDims.all(10.0),
|
| - margin: new EdgeDims.all(10.0),
|
| - decoration: new BoxDecoration(backgroundColor: const Color(0xFF6B6B6B)),
|
| - child: new Flex(
|
| - flexRows,
|
| - direction: FlexDirection.vertical,
|
| - key: 'flxv'));
|
| - }
|
| -
|
| - Widget buildToolBar() {
|
| - String banner = hasWon ?
|
| - 'Awesome!!' : alive ?
|
| - 'Mine Digger [$detectedCount-$totalMineCount]': 'Kaboom! [press here]';
|
| -
|
| - return new ToolBar(
|
| - // FIXME: Strange to have the toolbar be tapable.
|
| - center: new Listener(
|
| - onPointerDown: handleBannerPointerDown,
|
| - child: new Text(banner, style: Theme.of(this.app).text.title)
|
| - )
|
| - );
|
| - }
|
| -
|
| - Widget buildUI() {
|
| - // FIXME: We need to build the board before we build the toolbar because
|
| - // we compute the win state during build step.
|
| - Widget board = buildBoard();
|
| - return new Scaffold(
|
| - toolbar: buildToolBar(),
|
| - body: new Container(
|
| - child: new Center(child: board),
|
| - decoration: new BoxDecoration(backgroundColor: colors.Grey[50])
|
| - )
|
| - );
|
| - }
|
| -
|
| - void handleBannerPointerDown(sky.PointerEvent event) {
|
| - initialize();
|
| - app.setState((){});
|
| - }
|
| -
|
| - // User action. The user uncovers the cell which can cause losing the game.
|
| - void probe(int x, int y) {
|
| - if (!alive)
|
| - return;
|
| - if (uiState[y][x] == flaggedCell)
|
| - return;
|
| - // Allowed to probe.
|
| - if (cells[y][x]) {
|
| - // Probed on a mine --> dead!!
|
| - uiState[y][x] = explodedCell;
|
| - alive = false;
|
| - } else {
|
| - // No mine, uncover nearby if possible.
|
| - cull(x, y);
|
| - }
|
| - app.setState((){});
|
| - }
|
| -
|
| - // User action. The user is sure a mine is at this location.
|
| - void flag(int x, int y) {
|
| - if (uiState[y][x] == flaggedCell) {
|
| - uiState[y][x] = coveredCell;
|
| - --detectedCount;
|
| - } else {
|
| - uiState[y][x] = flaggedCell;
|
| - ++detectedCount;
|
| - }
|
| - app.setState((){});
|
| - }
|
| -
|
| - // Recursively uncovers cells whose totalMineCount is zero.
|
| - void cull(int x, int y) {
|
| - if ((x < 0) || (x > rows - 1))
|
| - return;
|
| - if ((y < 0) || (y > cols - 1))
|
| - return;
|
| -
|
| - if (uiState[y][x] == clearedCell)
|
| - return;
|
| - uiState[y][x] = clearedCell;
|
| -
|
| - if (mineCount(x, y) > 0)
|
| - return;
|
| -
|
| - cull(x - 1, y);
|
| - cull(x + 1, y);
|
| - cull(x, y - 1);
|
| - cull(x, y + 1 );
|
| - cull(x - 1, y - 1);
|
| - cull(x + 1, y + 1);
|
| - cull(x + 1, y - 1);
|
| - cull(x - 1, y + 1);
|
| - }
|
| -
|
| - int mineCount(int x, int y) {
|
| - int count = 0;
|
| - int my = cols - 1;
|
| - int mx = rows - 1;
|
| -
|
| - count += x > 0 ? bombs(x - 1, y) : 0;
|
| - count += x < mx ? bombs(x + 1, y) : 0;
|
| - count += y > 0 ? bombs(x, y - 1) : 0;
|
| - count += y < my ? bombs(x, y + 1 ) : 0;
|
| -
|
| - count += (x > 0) && (y > 0) ? bombs(x - 1, y - 1) : 0;
|
| - count += (x < mx) && (y < my) ? bombs(x + 1, y + 1) : 0;
|
| - count += (x < mx) && (y > 0) ? bombs(x + 1, y - 1) : 0;
|
| - count += (x > 0) && (y < my) ? bombs(x - 1, y + 1) : 0;
|
| -
|
| - return count;
|
| - }
|
| -
|
| - int bombs(int x, int y) {
|
| - return cells[y][x] ? 1 : 0;
|
| - }
|
| -}
|
| -
|
| -Widget makeCell(Widget widget) {
|
| - return new Container(
|
| - padding: new EdgeDims.all(1.0),
|
| - height: 27.0, width: 27.0,
|
| - decoration: new BoxDecoration(backgroundColor: const Color(0xFFC0C0C0)),
|
| - margin: new EdgeDims.all(2.0),
|
| - child: widget);
|
| -}
|
| -
|
| -Widget makeInnerCell(Widget widget) {
|
| - return new Container(
|
| - padding: new EdgeDims.all(1.0),
|
| - margin: new EdgeDims.all(3.0),
|
| - height: 17.0, width: 17.0,
|
| - child: widget);
|
| -}
|
| -
|
| -class CoveredMineNode extends Component {
|
| - final Game game;
|
| - final bool flagged;
|
| - final int posX;
|
| - final int posY;
|
| - Stopwatch stopwatch;
|
| -
|
| - CoveredMineNode(this.game, {this.flagged, this.posX, this.posY});
|
| -
|
| - void _handlePointerDown(sky.PointerEvent event) {
|
| - if (event.buttons == 1) {
|
| - game.probe(posX, posY);
|
| - } else if (event.buttons == 2) {
|
| - game.flag(posX, posY);
|
| - } else {
|
| - // Touch event.
|
| - stopwatch = new Stopwatch()..start();
|
| - }
|
| - }
|
| -
|
| - void _handlePointerUp(sky.PointerEvent event) {
|
| - if (stopwatch == null)
|
| - return;
|
| - // Pointer down was a touch event.
|
| - var ms = stopwatch.elapsedMilliseconds;
|
| - if (stopwatch.elapsedMilliseconds < 250) {
|
| - game.probe(posX, posY);
|
| - } else {
|
| - // Long press flags.
|
| - game.flag(posX, posY);
|
| - }
|
| - stopwatch = null;
|
| - }
|
| -
|
| - Widget build() {
|
| - Widget text = flagged ?
|
| - makeInnerCell(new StyledText(elements : [Game.textStyles[5], '\u2691'])) :
|
| - null;
|
| -
|
| - Container inner = new Container(
|
| - margin: new EdgeDims.all(2.0),
|
| - height: 17.0, width: 17.0,
|
| - decoration: new BoxDecoration(backgroundColor: const Color(0xFFD9D9D9)),
|
| - child: text);
|
| -
|
| - return makeCell(new Listener(
|
| - child: inner,
|
| - onPointerDown: _handlePointerDown,
|
| - onPointerUp: _handlePointerUp));
|
| - }
|
| -}
|
| -
|
| -class ExposedMineNode extends Component {
|
| - final int state;
|
| - final int count;
|
| -
|
| - ExposedMineNode({this.state, this.count});
|
| -
|
| - Widget build() {
|
| - StyledText text;
|
| - if (state == Game.clearedCell) {
|
| - // Uncovered cell with nearby mine count.
|
| - if (count != 0)
|
| - text = new StyledText(elements : [Game.textStyles[count], '$count']);
|
| - } else {
|
| - // Exploded mine or shown mine for 'game over'.
|
| - int color = state == Game.explodedCell ? 3 : 0;
|
| - text = new StyledText(elements : [Game.textStyles[color], '\u2600']);
|
| - }
|
| -
|
| - return makeCell(makeInnerCell(text));
|
| - }
|
| -}
|
| -
|
| -class MineDiggerApp extends App {
|
| - Game game;
|
| -
|
| - MineDiggerApp() {
|
| - game = new Game(this);
|
| - }
|
| -
|
| - Widget build() {
|
| - return game.buildUI();
|
| - }
|
| -}
|
| -
|
| -void main() {
|
| - runApp(new MineDiggerApp());
|
| -}
|
|
|