Skip to content

Commit

Permalink
Remove roles seed, reimplement choose roles algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
evgfilim1 committed Mar 26, 2024
1 parent 783ce31 commit 446eb26
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 189 deletions.
6 changes: 2 additions & 4 deletions lib/game/player.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import "dart:math";

import "package:meta/meta.dart";

import "../utils/extensions.dart";
Expand Down Expand Up @@ -88,9 +86,9 @@ class Player {

List<Player> generatePlayers({
List<String?>? nicknames,
Random? random,
List<PlayerRole>? roles,
}) {
final playerRoles = List.of(rolesList)..shuffle(random);
final playerRoles = roles ?? (List.of(rolesList)..shuffle());
return [
for (var i = 0; i < playerRoles.length; i++)
Player(
Expand Down
90 changes: 74 additions & 16 deletions lib/screens/choose_roles_screen.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import "dart:async";

import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:provider/provider.dart";

import "../game/player.dart";
import "../utils/db/repo.dart";
import "../utils/errors.dart";
import "../utils/extensions.dart";
import "../utils/find_seed.dart";
import "../utils/game_controller.dart";
import "../utils/ui.dart";
import "../widgets/confirmation_dialog.dart";
Expand All @@ -32,7 +30,6 @@ class _ChooseRolesScreenState extends State<ChooseRolesScreen> {
(_) => PlayerRole.values.toSet(),
growable: false,
);
var _isInProgress = false;
final _errorsByRole = <PlayerRole, _ValidationErrorType>{};
final _errorsByIndex = <int>{};
final _chosenNicknames = List<String?>.generate(rolesList.length, (index) => null);
Expand Down Expand Up @@ -110,30 +107,93 @@ class _ChooseRolesScreenState extends State<ChooseRolesScreen> {
..addAll(byIndex);
}

List<PlayerRole>? _randomizeRoles() {
final results = <List<PlayerRole>>[];
final count = rolesList.length;
for (var iDon = 0; iDon < count; iDon++) {
if (!_roles[iDon].contains(PlayerRole.don)) {
continue;
}
for (var iSheriff = 0; iSheriff < count; iSheriff++) {
if (!_roles[iSheriff].contains(PlayerRole.sheriff) || iSheriff == iDon) {
continue;
}
for (var iMafia = 0; iMafia < count; iMafia++) {
if (!_roles[iMafia].contains(PlayerRole.mafia) || iMafia == iDon || iMafia == iSheriff) {
continue;
}
for (var jMafia = iMafia + 1; jMafia < count; jMafia++) {
if (!_roles[jMafia].contains(PlayerRole.mafia) ||
jMafia == iDon ||
jMafia == iSheriff) {
continue;
}
var valid = true;
for (var iCitizen = 0; iCitizen < count; iCitizen++) {
if (iCitizen == iDon ||
iCitizen == iSheriff ||
iCitizen == iMafia ||
iCitizen == jMafia) {
continue;
}
if (!_roles[iCitizen].contains(PlayerRole.citizen)) {
valid = false;
break;
}
}
if (valid) {
results.add([
for (var i = 0; i < count; i++)
i == iDon
? PlayerRole.don
: i == iSheriff
? PlayerRole.sheriff
: i == iMafia || i == jMafia
? PlayerRole.mafia
: PlayerRole.citizen,
]);
}
}
}
}
}
if (results.isEmpty) {
return null;
}
final result = results.randomElement;
assert(
() {
for (var i = 0; i < rolesList.length; i++) {
if (!_roles[i].contains(result[i])) {
return false;
}
}
return true;
}(),
"Roles are invalid",
);
return result;
}

Future<void> _onFabPressed(BuildContext context) async {
setState(_validate);
if (_errorsByIndex.isNotEmpty || _errorsByRole.isNotEmpty) {
showSnackBar(context, const SnackBar(content: Text("Для продолжения исправьте ошибки")));
return;
}
final controller = context.read<GameController>();
final initialSeed = controller.rolesSeed ?? getNewSeed();
setState(() => _isInProgress = true);
final newSeed = await compute(findSeedIsolateWrapper, (initialSeed, _roles));
setState(() => _isInProgress = false);
if (!context.mounted) {
throw ContextNotMountedError();
}
if (newSeed == null) {
final newRoles = _randomizeRoles();
if (newRoles == null) {
showSnackBar(
context,
const SnackBar(content: Text("Невозможно применить выбранные роли")),
);
return;
}
controller
..rolesSeed = newSeed
..nicknames = _chosenNicknames;
..roles = newRoles
..nicknames = _chosenNicknames
..startNewGame();
final showRoles = await showDialog<bool>(
context: context,
builder: (context) => const ConfirmationDialog(
Expand Down Expand Up @@ -273,9 +333,7 @@ class _ChooseRolesScreenState extends State<ChooseRolesScreen> {
floatingActionButton: FloatingActionButton(
tooltip: "Применить",
onPressed: () => _onFabPressed(context),
child: _isInProgress
? const SizedBox.square(dimension: 24, child: CircularProgressIndicator())
: const Icon(Icons.check),
child: const Icon(Icons.check),
),
);
}
Expand Down
103 changes: 32 additions & 71 deletions lib/screens/debug_menu_screen.dart
Original file line number Diff line number Diff line change
@@ -1,85 +1,46 @@
import "package:flutter/material.dart";
import "package:provider/provider.dart";

import "../utils/errors.dart";
import "../utils/game_controller.dart";
import "../utils/ui.dart";
import "../widgets/confirmation_dialog.dart";
import "../widgets/list_tiles/text_field.dart";

class DebugMenuScreen extends StatelessWidget {
const DebugMenuScreen({super.key});

@override
Widget build(BuildContext context) {
final controller = context.watch<GameController>();
return Scaffold(
appBar: AppBar(title: const Text("Меню отладки")),
body: ListView(
children: [
TextFieldListTile(
leading: const Icon(Icons.casino),
title: const Text("Зерно генератора ролей"),
subtitle: const Text("При изменении игра будет перезапущена"),
keyboardType: TextInputType.number,
initialText: controller.rolesSeed?.toString() ?? "",
labelText: "Зерно генератора ролей",
onSubmit: (value) async {
final seed = int.tryParse(value);
if (seed != null) {
final res = await showDialog<bool>(
context: context,
builder: (context) => const ConfirmationDialog(
title: Text("Применить настройки?"),
content: Text("Текущая игра будет перезапущена. Продолжить?"),
),
);
if (res ?? false) {
controller
..rolesSeed = seed
..stopGame();
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text("Меню отладки")),
body: ListView(
children: [
TextFieldListTile(
leading: const Icon(Icons.arrow_forward),
title: const Text("Перейти к экрану"),
initialText: "/",
labelText: "Путь",
validator: (value) {
if (value == null || value.isEmpty) {
return "Введите путь";
}
if (!value.startsWith("/")) {
return "Путь должен начинаться с /";
}
return null;
},
onSubmit: (value) async {
try {
await Navigator.pushNamed(context, value);
} catch (e) {
if (!context.mounted) {
throw ContextNotMountedError();
return;
}
showSnackBar(
context,
const SnackBar(content: Text("Игра перезапущена")),
await showSimpleDialog(
context: context,
title: const Text("Ошибка"),
content: Text(e.toString()),
);
}
}
},
),
TextFieldListTile(
leading: const Icon(Icons.arrow_forward),
title: const Text("Перейти к экрану"),
initialText: "/",
labelText: "Путь",
validator: (value) {
if (value == null || value.isEmpty) {
return "Введите путь";
}
if (!value.startsWith("/")) {
return "Путь должен начинаться с /";
}
return null;
},
onSubmit: (value) async {
try {
await Navigator.pushNamed(context, value);
} catch (e) {
if (!context.mounted) {
return;
}
await showSimpleDialog(
context: context,
title: const Text("Ошибка"),
content: Text(e.toString()),
);
}
},
),
],
),
);
}
},
),
],
),
);
}
76 changes: 31 additions & 45 deletions lib/screens/roles.dart
Original file line number Diff line number Diff line change
@@ -1,64 +1,50 @@
import "dart:math";

import "package:flutter/material.dart";
import "package:provider/provider.dart";

import "../game/player.dart";
import "../utils/game_controller.dart";
import "../utils/log.dart";
import "../utils/ui.dart";

class RolesScreen extends StatelessWidget {
static final _log = Logger("RolesScreen");

const RolesScreen({super.key});

@override
Widget build(BuildContext context) {
final controller = context.watch<GameController>();
final players = controller.isGameInitialized
? controller.players
: controller.rolesSeed != null
? generatePlayers(random: Random(controller.rolesSeed), nicknames: controller.nicknames)
: const <Player>[];
if (players.isEmpty) {
_log.warning("Players is empty");
}
final players = controller.players;
assert(players.isNotEmpty, "Players must be non-empty. Is the game running?");
return Scaffold(
appBar: AppBar(
title: const Text("Раздача ролей"),
),
body: players.isNotEmpty
? PageView.builder(
itemCount: players.length,
itemBuilder: (context, index) {
final player = players[index];
final String topText;
if (player.nickname != null) {
topText = "${player.nickname} (Игрок #${player.number})";
} else {
topText = "Игрок #${player.number}";
}
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
topText,
style: const TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
Text(
player.role.prettyName,
style: const TextStyle(fontSize: 48),
textAlign: TextAlign.center,
),
],
),
);
},
)
: const Center(child: Text(r"¯\_(ツ)_/¯", style: TextStyle(fontSize: 48))),
body: PageView.builder(
itemCount: players.length,
itemBuilder: (context, index) {
final player = players[index];
final String topText;
if (player.nickname != null) {
topText = "${player.nickname} (Игрок #${player.number})";
} else {
topText = "Игрок #${player.number}";
}
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
topText,
style: const TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
Text(
player.role.prettyName,
style: const TextStyle(fontSize: 48),
textAlign: TextAlign.center,
),
],
),
);
},
),
);
}
}
5 changes: 2 additions & 3 deletions lib/screens/settings/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,8 @@ class SettingsScreen extends StatelessWidget {
showSimpleDialog(
context: context,
title: const Text("Сообщить о проблеме"),
content: Text(
"Для сообщения о проблеме нужно поделиться файлом мне в ЛС в Telegram.\n\n"
"Зерно генерации ролей: ${controller.rolesSeed}",
content: const Text(
"Для сообщения о проблеме нужно поделиться файлом мне в ЛС в Telegram.",
),
extraActions: [
TextButton(
Expand Down
Loading

0 comments on commit 446eb26

Please sign in to comment.