Skip to content

Instantly share code, notes, and snippets.

@jaysephjw
Last active November 2, 2022 02:14
Show Gist options
  • Save jaysephjw/07813c001233da22954038ff7263e1ff to your computer and use it in GitHub Desktop.
Save jaysephjw/07813c001233da22954038ff7263e1ff to your computer and use it in GitHub Desktop.
Eight Queens in Flutter!
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show listEquals;
/// Fun with the 8 queens problem in Flutter.
/// View me online @ https://dartpad.dev/?id=07813c001233da22954038ff7263e1ff
/// -------------------------
/// Dart entry-point
/// -------------------------
void main() {
runApp(const MyApp());
}
/// -------------------------
/// App
/// -------------------------
// const double boardSizePixels = 200;
const int queenCount = 8;
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
scrollbarTheme: ScrollbarThemeData(
trackVisibility: MaterialStateProperty.all(true),
// trackColor: MaterialStateProperty.all(Colors.black),
thumbVisibility: MaterialStateProperty.all(true),
),
primarySwatch: Colors.red,
),
home: const QueensPage(title: '$queenCount Queens', queenCount: queenCount),
);
}
}
class QueensPage extends StatefulWidget {
const QueensPage({Key? key, required this.title, required this.queenCount}) : super(key: key);
final String title;
final int queenCount;
@override
State<QueensPage> createState() => _QueensPageState();
}
class _QueensPageState extends State<QueensPage> {
List<List<int>> solutions = [];
bool done = false;
ScrollController scrollController = ScrollController();
@override
initState() {
super.initState();
findSolutions();
}
void findSolutions() {
final iterator = solveQueens(queenCount, [], {}, {}).iterator;
findNext(iterator);
}
void findNext(Iterator iterator) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (iterator.moveNext()) {
setState(() {
solutions.add(iterator.current);
scrollController.jumpTo(scrollController.position.maxScrollExtent);
});
findNext(iterator);
} else {
setState(() {
done = true;
});
}
});
}
void output(solution) {
debugPrint('Found solution $solution');
setState(() {
solutions.add(solution);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: const EdgeInsets.all(12),
alignment: Alignment.center,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Align(
alignment: Alignment.centerLeft,
child: Text('${solutions.length} solutions found${!done ? '...' : ''}', style: Theme.of(context).textTheme.titleLarge)
),
),
Expanded(
child: SingleChildScrollView(
controller: scrollController,
child: GridView.count(
shrinkWrap: true,
crossAxisCount: queenCount,
children: solutions
.map((queens) => SolutionCard(queens: queens)).toList()
),
),
),
],
),
)
);
}
}
/// ------------------a-------
/// "N-Queens" generator function
/// -------------------------
Iterable<List<int>> solveQueens(n, List<int> positions, Set diags1, Set diags2) sync* {
if (positions.length == n) {
// We found a solution!
yield positions;
} else {
final col = positions.length - 1;
for (int row = 0; row < n; row++) {
final d1 = row + col;
final d2 = row - col;
if (!positions.contains(row) && !diags1.contains(d1) && !diags2.contains(d2)) {
// Valid placement; place here and iterate.
yield* solveQueens(n, positions + [row], {...diags1, d1}, {...diags2, d2});
}
}
}
}
/// -------------------------
/// Helper widgets
/// -------------------------
class SolutionCard extends StatelessWidget {
final List<int> queens;
const SolutionCard({Key? key, required this.queens}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(4),
// constraints: const BoxConstraints(maxWidth: boardSizePixels, maxHeight: boardSizePixels),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(queens.toString()),
const SizedBox(height: 4),
Expanded(child: ChessBoard(queens: queens)),
],
),
);
}
}
class ChessBoard extends StatelessWidget {
final List<int?> queens; // [0,3,2] List of row positions from top left offsets
const ChessBoard({Key? key, required this.queens}) : super(key: key);
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1,
child: CustomPaint(
painter: BoardPainter(queens.length, queens),
),
);
}
}
class BoardPainter extends CustomPainter {
/// The board size, e.g. n = 8
final int n;
final List<int?> queens; // [0,3,2] List of row positions from top left offsets
BoardPainter(this.n, this.queens) : assert(queens.length == n) ;
static final Paint lightPaint = Paint()
..color = const Color.fromARGB(255, 255, 207, 159)
..style = PaintingStyle.fill
;
static final Paint darkPaint = Paint()
..color = const Color.fromARGB(255, 210, 140, 69)
..style = PaintingStyle.fill
;
static final Paint queenPaint = Paint()
..color = const Color.fromARGB(255, 0, 0, 0)
..style = PaintingStyle.fill
;
static Paint borderPaint = Paint()
..color = Colors.black26
..strokeWidth = 1
..style = PaintingStyle.stroke
;
@override
void paint(Canvas canvas, Size size) {
// Draw grid of [width] into [size].
// Assume square!
final w = size.width / n;
for (int x = 0; x < n; x++) {
for (int y = 0; y < n; y++) {
final hasQueen = queens[x] == y;
final fillPaint = (x + y).isEven ? lightPaint : darkPaint;
canvas.drawRect(Rect.fromLTWH(x*w, y*w, w, w), fillPaint);
canvas.drawRect(Rect.fromLTWH(x*w, y*w, w, w), borderPaint);
if (hasQueen) {
canvas.drawRect(Rect.fromLTWH(x*w, y*w, w, w), queenPaint);
}
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate is BoardPainter && listEquals(queens, oldDelegate.queens) && n == oldDelegate.n;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment