Last active
November 2, 2022 02:14
-
-
Save jaysephjw/07813c001233da22954038ff7263e1ff to your computer and use it in GitHub Desktop.
Eight Queens in Flutter!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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