Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 49 additions & 82 deletions lib/dash_sweeper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,10 @@ class _DashSweeperState extends State<DashSweeper> {
final dashAmount = amount ~/ 10; // TODO customize percentage
final emptyAmount = amount - dashAmount;

// create tiles
final dashTiles = List.generate(
dashAmount,
(_) => DashTile(TileState.idle),
);
final emptyTiles = List.generate(
emptyAmount,
(_) => EmptyTile(TileState.idle),
);
tiles = [...dashTiles, ...emptyTiles]..shuffle();
tiles = [
for (var _ in dashAmount.thisLengthList) DashTile(TileState.idle),
for (var _ in emptyAmount.thisLengthList) EmptyTile(TileState.idle),
]..shuffle();
}

bool inBounds(int row, int column) {
Expand All @@ -48,79 +42,27 @@ class _DashSweeperState extends State<DashSweeper> {
column < widget.columns;
}

int? getIndex(int row, int column) {
int? getIndex(NamedPoint point) {
var (:row, :column) = point;
if (!inBounds(row, column)) return null;
return (row * widget.columns) + column;
}

(int row, int column) getPointForIndex(int index) {
NamedPoint getPointForIndex(int index) {
final row = (index / widget.columns).floor();
final column = index % widget.columns;
return (row, column);
}

int? topLeftIndex(int row, int column) {
final targetRow = row - 1;
final targetColumn = column - 1;
return getIndex(targetRow, targetColumn);
}

int? topCenterIndex(int row, int column) {
final targetRow = row - 1;
return getIndex(targetRow, column);
}

int? topRightIndex(int row, int column) {
final targetRow = row - 1;
final targetColumn = column + 1;
return getIndex(targetRow, targetColumn);
return (row: row, column: column);
}

int? leftIndex(int row, int column) {
final targetColumn = column - 1;
return getIndex(row, targetColumn);
}

int? rightIndex(int row, int column) {
final targetColumn = column + 1;
return getIndex(row, targetColumn);
}

int? bottomLeftIndex(int row, int column) {
final targetRow = row + 1;
final targetColumn = column - 1;
return getIndex(targetRow, targetColumn);
}

int? bottomCenterIndex(int row, int column) {
final targetRow = row + 1;
return getIndex(targetRow, column);
}

int? bottomRightIndex(int row, int column) {
final targetRow = row + 1;
final targetColumn = column + 1;
return getIndex(targetRow, targetColumn);
}

List<int> getNeighbouringDashes(int index) {
return getNeighbouringIndeces(
index,
).where((tileIndex) => tiles[tileIndex] is DashTile).toList();
int countNeighbouringDashes(int index) {
return getNeighbouringIndeces(index) // formatting hack
.where((tileIndex) => tiles[tileIndex] is DashTile)
.length;
}

Iterable<int> getNeighbouringIndeces(int index) {
final (row, column) = getPointForIndex(index);
return [
topLeftIndex(row, column),
topCenterIndex(row, column),
topRightIndex(row, column),
leftIndex(row, column),
rightIndex(row, column),
bottomLeftIndex(row, column),
bottomCenterIndex(row, column),
bottomRightIndex(row, column),
].nonNulls;
final point = getPointForIndex(index);
return point.adjacents.map(getIndex).nonNulls;
}

void handleOnLongPress(int index) {
Expand All @@ -139,7 +81,7 @@ class _DashSweeperState extends State<DashSweeper> {

void handleOnTap(int index) {
final tile = tiles[index];
if (tile.state != TileState.idle) return;
if (!tile.state.isIdle) return;

if (tile is DashTile) {
_gameOver();
Expand Down Expand Up @@ -236,8 +178,8 @@ class _DashSweeperState extends State<DashSweeper> {
toReveal.add(index);
checked.add(index);

final dashes = getNeighbouringDashes(index);
if (dashes.isNotEmpty) return toReveal;
final dashes = countNeighbouringDashes(index);
if (dashes > 0) return toReveal;

final neighbours = getNeighbouringIndeces(index);
if (checked.containsAll(neighbours)) return toReveal;
Expand Down Expand Up @@ -265,7 +207,7 @@ class _DashSweeperState extends State<DashSweeper> {
onLongPress: () => handleOnLongPress(index),
),
EmptyTile(:final state) => EmptyTileItem(
amount: getNeighbouringDashes(index).length,
amount: countNeighbouringDashes(index),
state: state,
onTap: () => handleOnTap(index),
onLongPress: () => handleOnLongPress(index),
Expand All @@ -277,7 +219,15 @@ class _DashSweeperState extends State<DashSweeper> {
}
}

enum TileState { idle, revealed, flagged }
enum TileState {
idle,
revealed,
flagged;

bool get isIdle => this == TileState.idle;
bool get isRevealed => this == TileState.revealed;
bool get isFlagged => this == TileState.flagged;
}

sealed class Tile {
final TileState state;
Expand Down Expand Up @@ -375,18 +325,18 @@ class TileItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
elevation: state != TileState.idle ? 0 : 4,
elevation: state.isIdle ? 4 : 0,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: InkWell(
onTap: onTap,
onLongPress: onLongPress,
onTap: state.isIdle ? onTap : null,
onLongPress: state.isIdle ? onLongPress : null,
child: Center(
child: AnimatedSwitcher(
duration: Duration(milliseconds: 300),
child: switch (state) {
TileState.idle => SizedBox(),
TileState.idle => const SizedBox.shrink(),
TileState.revealed => child,
TileState.flagged => Icon(
TileState.flagged => const Icon(
Icons.flag,
color: Colors.red,
),
Expand All @@ -397,3 +347,20 @@ class TileItem extends StatelessWidget {
);
}
}

typedef NamedPoint = ({int row, int column});

extension on int {
List<int> get adjacents => [this - 1, this, this + 1];

List<int> get thisLengthList => [for (var i = 0; i < this; i++) i];
}

extension on NamedPoint {
List<NamedPoint> get adjacents {
return [
for (var row in row.adjacents)
for (var column in column.adjacents) (row: row, column: column),
];
}
}