diff --git a/android/gradle.properties b/android/gradle.properties index ed86f0f26922ea79f4d0c8e0e84f6d74259952bb..3487476dc637023e34426ce50caf3343f91e3038 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.22 -app.versionCode=22 +app.versionName=0.0.23 +app.versionCode=23 diff --git a/fastlane/metadata/android/en-US/changelogs/23.txt b/fastlane/metadata/android/en-US/changelogs/23.txt new file mode 100644 index 0000000000000000000000000000000000000000..4746db60a14b3e1cb08d3b752020a7f33a45fc26 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/23.txt @@ -0,0 +1 @@ +Add choose categories theme. diff --git a/fastlane/metadata/android/fr-FR/changelogs/23.txt b/fastlane/metadata/android/fr-FR/changelogs/23.txt new file mode 100644 index 0000000000000000000000000000000000000000..f592cb695abdc80c0c78c1a231148e8f82ed450a --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/23.txt @@ -0,0 +1 @@ +Ajout du choix du thème des catégories. diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart index c4486031cf4e7ed40d95822ce17ee826fe84e4b2..72aed12fae08e985e25fd473bb66252da552fb46 100644 --- a/lib/config/default_game_settings.dart +++ b/lib/config/default_game_settings.dart @@ -1,8 +1,10 @@ +import 'package:sortgame/data/fetch_data_helper.dart'; import 'package:sortgame/utils/tools.dart'; class DefaultGameSettings { static const List<String> availableParameters = [ 'itemsCount', + 'theme', ]; static const int itemsCountValueLow = 5; @@ -18,12 +20,20 @@ class DefaultGameSettings { itemsCountValueVeryHigh, ]; + static const int defaultThemeValue = 0; + static List<int> getAvailableValues(String parameterCode) { switch (parameterCode) { case 'itemsCount': return DefaultGameSettings.allowedItemsCountValues; } + switch (parameterCode) { + case 'theme': + final int count = FetchDataHelper().getThemes().length; + return List<int>.generate(count, (i) => i); + } + printlog('Did not find any available value for game parameter "$parameterCode".'); return []; } diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart index a2c04ea96f915f709c25b31942c277bb742ade54..c411ae194035e0dece266f3434a7358cc3c55c19 100644 --- a/lib/cubit/settings_game_cubit.dart +++ b/lib/cubit/settings_game_cubit.dart @@ -12,11 +12,13 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { void setValues({ int? itemsCount, + int? theme, }) { emit( GameSettingsState( settings: GameSettings( itemsCount: itemsCount ?? state.settings.itemsCount, + theme: theme ?? state.settings.theme, ), ), ); @@ -26,6 +28,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { switch (code) { case 'itemsCount': return GameSettings.getItemsCountValueFromUnsafe(state.settings.itemsCount); + case 'theme': + return GameSettings.getThemeValueFromUnsafe(state.settings.theme); } return 0; } @@ -35,19 +39,23 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { printlog('code: $code / value: $value'); int itemsCount = code == 'itemsCount' ? value : getParameterValue('itemsCount'); + int theme = code == 'theme' ? value : getParameterValue('theme'); setValues( itemsCount: itemsCount, + theme: theme, ); } @override GameSettingsState? fromJson(Map<String, dynamic> json) { int itemsCount = json['itemsCount'] as int; + int theme = json['theme'] as int; return GameSettingsState( settings: GameSettings( itemsCount: itemsCount, + theme: theme, ), ); } @@ -56,6 +64,7 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { Map<String, dynamic>? toJson(GameSettingsState state) { return <String, dynamic>{ 'itemsCount': state.settings.itemsCount, + 'theme': state.settings.theme, }; } } diff --git a/lib/data/fetch_data_helper.dart b/lib/data/fetch_data_helper.dart index fd7ac7ad4b769668e49a2174191195cab43eb513..9904c8bd319da185eac0194522624123f5773402 100644 --- a/lib/data/fetch_data_helper.dart +++ b/lib/data/fetch_data_helper.dart @@ -1,7 +1,9 @@ import 'package:sortgame/data/game_data.dart'; import 'package:sortgame/models/data/category.dart'; import 'package:sortgame/models/data/game_item.dart'; +import 'package:sortgame/models/data/game_theme.dart'; import 'package:sortgame/models/data/item.dart'; +import 'package:sortgame/models/settings_game.dart'; import 'package:sortgame/utils/tools.dart'; class FetchDataHelper { @@ -13,6 +15,9 @@ class FetchDataHelper { final List<Item> _items = []; List<Item> get items => _items; + final List<GameTheme> _themes = []; + List<GameTheme> get themes => _themes; + final List<GameItem> _mapping = []; void init() { @@ -25,6 +30,16 @@ class FetchDataHelper { _categories.add(Category(key: element, text: element)); } + final Map<String, dynamic> rawThemes = gameData['themes'] as Map<String, dynamic>; + rawThemes.forEach((code, rawCategories) { + final List<Category> categories = []; + for (var rawElement in rawCategories) { + final element = rawElement.toString(); + categories.add(Category(key: element, text: element)); + } + _themes.add(GameTheme(code: code, categories: categories)); + }); + final List<dynamic> rawItems = gameData['items'] as List<dynamic>; for (var rawElement in rawItems) { final element = rawElement.toString(); @@ -64,13 +79,27 @@ class FetchDataHelper { } } - List<GameItem> getItems(int count) { + List<GameItem> getItems(GameSettings gameSettings) { if (_mapping.isEmpty) { init(); } + final int count = gameSettings.itemsCount; + final int theme = gameSettings.theme; + List<GameItem> items = _mapping; + // Remove unwanted categories if theme is selected + if (theme != 0) { + final GameTheme gameTheme = _themes[theme]; + for (GameItem item in items) { + item.isCategory.removeWhere((Category category) => + (!gameTheme.categories.map((c) => c.key).contains(category.key))); + item.isNotCategory.removeWhere((Category category) => + (!gameTheme.categories.map((c) => c.key).contains(category.key))); + } + } + // Remove items without enough data items.removeWhere((GameItem gameItem) => (gameItem.isCategory.isEmpty || gameItem.isNotCategory.isEmpty)); @@ -79,4 +108,20 @@ class FetchDataHelper { return items.take(count).toList(); } + + List<GameTheme> getThemes() { + if (_themes.isEmpty) { + init(); + } + + return _themes.toList(); + } + + GameTheme getTheme(int themeIndex) { + if (_themes.isEmpty) { + init(); + } + + return _themes[themeIndex]; + } } diff --git a/lib/data/game_data.dart b/lib/data/game_data.dart index a28b980432f11277a616ef11e76a2d660ec86724..99b5ea76cfa0f5a1515ee45eb7cb6f674f5fccd6 100644 --- a/lib/data/game_data.dart +++ b/lib/data/game_data.dart @@ -15,6 +15,12 @@ class GameData { ["inerte", "animal", "végétal"], ["naturel", "artificiel"] ], + "themes": { + "tout": [], + "état": ["liquide", "solide", "gazeux"], + "genre": ["inerte", "animal", "végétal"], + "vivant": ["animal", "végétal"] + }, "items": [ "ABEILLE", "AIGUILLE", @@ -372,8 +378,8 @@ class GameData { "na": [] }, "BATEAU": { - "is": ["animal", "inerte"], - "isnot": ["végétal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "BAVOIR": { @@ -477,8 +483,8 @@ class GameData { "na": [] }, "CAMION": { - "is": ["inerte", "végétal"], - "isnot": ["animal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "CANARD": { @@ -522,8 +528,8 @@ class GameData { "na": [] }, "CHAISE": { - "is": ["animal", "artificiel", "inerte", "solide"], - "isnot": ["gazeux", "liquide", "naturel", "végétal"], + "is": ["artificiel", "inerte", "solide"], + "isnot": ["animal", "gazeux", "liquide", "naturel", "végétal"], "na": [] }, "CHAMPIGNON": { @@ -537,8 +543,8 @@ class GameData { "na": [] }, "CHAUSSETTE": { - "is": ["inerte", "végétal"], - "isnot": ["animal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "CHAUSSURE": { @@ -717,8 +723,8 @@ class GameData { "na": [] }, "FROMAGE": { - "is": ["animal", "artificiel", "inerte"], - "isnot": ["liquide", "naturel", "végétal"], + "is": ["artificiel", "inerte"], + "isnot": ["animal", "liquide", "naturel", "végétal"], "na": [] }, "FUSÉE": { @@ -877,8 +883,8 @@ class GameData { "na": [] }, "LUNETTES": { - "is": ["animal", "inerte"], - "isnot": ["végétal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "MAISON": { @@ -957,8 +963,8 @@ class GameData { "na": [] }, "OREILLER": { - "is": ["animal", "inerte"], - "isnot": ["végétal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "OURS": { @@ -967,8 +973,8 @@ class GameData { "na": ["liquide", "solide"] }, "PAIN": { - "is": ["inerte", "végétal"], - "isnot": ["animal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "PANDA": { @@ -1052,8 +1058,8 @@ class GameData { "na": [] }, "PISCINE": { - "is": ["inerte", "végétal"], - "isnot": ["animal", "gazeux"], + "is": ["inerte"], + "isnot": ["animal", "gazeux", "végétal"], "na": [] }, "PIVERT": { @@ -1067,8 +1073,8 @@ class GameData { "na": [] }, "PLAGE": { - "is": ["inerte", "naturel", "végétal"], - "isnot": ["animal", "artificiel"], + "is": ["inerte", "naturel"], + "isnot": ["animal", "artificiel", "végétal"], "na": [] }, "POIRE": { @@ -1177,8 +1183,8 @@ class GameData { "na": ["inerte", "végétal"] }, "ROBINET": { - "is": ["animal", "inerte"], - "isnot": ["végétal"], + "is": ["inerte"], + "isnot": ["animal", "végétal"], "na": [] }, "ROSE": { @@ -1277,8 +1283,8 @@ class GameData { "na": [] }, "SOUS-MARIN": { - "is": ["artificiel", "inerte", "végétal"], - "isnot": ["animal", "naturel"], + "is": ["artificiel", "inerte"], + "isnot": ["animal", "naturel", "végétal"], "na": [] }, "STADE": { @@ -1302,8 +1308,8 @@ class GameData { "na": [] }, "TABOURET": { - "is": ["animal", "artificiel", "inerte", "solide"], - "isnot": ["gazeux", "liquide", "naturel", "végétal"], + "is": ["artificiel", "inerte", "solide"], + "isnot": ["animal", "gazeux", "liquide", "naturel", "végétal"], "na": [] }, "TACHE": { @@ -1367,8 +1373,8 @@ class GameData { "na": [] }, "TOURNEVIS": { - "is": ["animal", "inerte"], - "isnot": ["gazeux", "végétal"], + "is": ["inerte"], + "isnot": ["animal", "gazeux", "végétal"], "na": [] }, "TRACTEUR": { @@ -1422,8 +1428,8 @@ class GameData { "na": [] }, "VOILIER": { - "is": ["animal", "artificiel", "inerte", "solide"], - "isnot": ["gazeux", "liquide", "naturel", "végétal"], + "is": ["artificiel", "inerte", "solide"], + "isnot": ["animal", "gazeux", "liquide", "naturel", "végétal"], "na": [] }, "VOITURE": { diff --git a/lib/models/data/game_theme.dart b/lib/models/data/game_theme.dart new file mode 100644 index 0000000000000000000000000000000000000000..e0ecc61546af53c8555bfa01e4ddd09043dbc171 --- /dev/null +++ b/lib/models/data/game_theme.dart @@ -0,0 +1,23 @@ +import 'package:sortgame/models/data/category.dart'; + +class GameTheme { + final String code; + final List<Category> categories; + + GameTheme({ + required this.code, + required this.categories, + }); + + @override + String toString() { + return '$GameTheme(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + 'code': code, + 'categories': categories.toString(), + }; + } +} diff --git a/lib/models/game.dart b/lib/models/game.dart index fcc9097db07c42cc69326884dc8032b799c4a720..74e9d0254eafc47b90eaa2ebb3a97e9a724332bb 100644 --- a/lib/models/game.dart +++ b/lib/models/game.dart @@ -38,7 +38,7 @@ class Game { GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); - List<GameItem> items = FetchDataHelper().getItems(newGameSettings.itemsCount); + List<GameItem> items = FetchDataHelper().getItems(newGameSettings); return Game( items: items, diff --git a/lib/models/settings_game.dart b/lib/models/settings_game.dart index 67681ec8b9a761986cf41cc0db681fb39f82b8e0..3528fa4b15bfde064fb15d132c027e53e4127e8c 100644 --- a/lib/models/settings_game.dart +++ b/lib/models/settings_game.dart @@ -3,9 +3,11 @@ import 'package:sortgame/utils/tools.dart'; class GameSettings { final int itemsCount; + final int theme; GameSettings({ required this.itemsCount, + required this.theme, }); static int getItemsCountValueFromUnsafe(int itemsCount) { @@ -16,15 +18,25 @@ class GameSettings { return DefaultGameSettings.defaultItemsCountValue; } + static int getThemeValueFromUnsafe(int theme) { + if (DefaultGameSettings.getAvailableValues('theme').contains(theme)) { + return theme; + } + + return DefaultGameSettings.defaultThemeValue; + } + factory GameSettings.createDefault() { return GameSettings( itemsCount: DefaultGameSettings.defaultItemsCountValue, + theme: DefaultGameSettings.defaultThemeValue, ); } void dump() { printlog('Settings: '); printlog(' itemsCount: $itemsCount'); + printlog(' theme: $theme'); } @override @@ -35,6 +47,7 @@ class GameSettings { Map<String, dynamic>? toJson() { return <String, dynamic>{ 'itemsCount': itemsCount, + 'theme': theme, }; } } diff --git a/lib/ui/painters/parameter_painter.dart b/lib/ui/painters/parameter_painter.dart index fd4e3921d5fdecf4812a263148c4f1a3641b2c4e..3444bb36b1a44617b7c8fcb45560acec57ea1ee8 100644 --- a/lib/ui/painters/parameter_painter.dart +++ b/lib/ui/painters/parameter_painter.dart @@ -3,6 +3,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:sortgame/config/default_game_settings.dart'; +import 'package:sortgame/data/fetch_data_helper.dart'; +import 'package:sortgame/models/data/game_theme.dart'; import 'package:sortgame/models/settings_game.dart'; import 'package:sortgame/models/settings_global.dart'; import 'package:sortgame/utils/tools.dart'; @@ -44,6 +46,9 @@ class ParameterPainter extends CustomPainter { case 'itemsCount': paintItemsCountParameterItem(value, canvas, canvasSize); break; + case 'theme': + paintThemeParameterItem(value, canvas, canvasSize); + break; default: printlog('Unknown parameter: $code/$value'); paintUnknownParameterItem(value, canvas, canvasSize); @@ -146,4 +151,46 @@ class ParameterPainter extends CustomPainter { ), ); } + + void paintThemeParameterItem( + final int value, + final Canvas canvas, + final double size, + ) { + final GameTheme theme = FetchDataHelper().getTheme(value); + + final Color backgroundColor = + Color((theme.code.hashCode * 0xFFFFFF).toInt()).withOpacity(1.0); + + final paint = Paint(); + paint.strokeJoin = StrokeJoin.round; + paint.strokeWidth = 3 / 100 * size; + + // Colored background + paint.color = backgroundColor; + paint.style = PaintingStyle.fill; + canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint); + + // centered text value + final textSpan = TextSpan( + text: theme.code, + style: TextStyle( + color: Colors.black, + fontSize: size / 4, + fontWeight: FontWeight.bold, + ), + ); + final textPainter = TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + ); + textPainter.layout(); + textPainter.paint( + canvas, + Offset( + (size - textPainter.width) * 0.5, + (size - textPainter.height) * 0.5, + ), + ); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 220bed61660eee38dc7609059903093f782f0aa6..c1e8bb481e911e1deb2ca41a9b38c58c5c91fa7b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A sorting game application. publish_to: 'none' -version: 0.0.22+22 +version: 0.0.23+23 environment: sdk: '^3.0.0' diff --git a/scripts/data.json b/scripts/data.json index a78c464bd8a89caf6b599ff02640e30f10727617..a3d3d25c65103e8be49102b7e61dd89906e2c6ba 100644 --- a/scripts/data.json +++ b/scripts/data.json @@ -25,6 +25,23 @@ "artificiel" ] ], + "themes": { + "tout": [], + "état": [ + "liquide", + "solide", + "gazeux" + ], + "genre": [ + "inerte", + "animal", + "végétal" + ], + "vivant": [ + "animal", + "végétal" + ] + }, "items": [ "ABEILLE", "AIGUILLE", @@ -547,10 +564,10 @@ }, "BATEAU": { "is": [ - "animal", "inerte" ], "isnot": [ + "animal", "végétal" ], "na": [] @@ -798,11 +815,11 @@ }, "CAMION": { "is": [ - "inerte", - "végétal" + "inerte" ], "isnot": [ - "animal" + "animal", + "végétal" ], "na": [] }, @@ -905,12 +922,12 @@ }, "CHAISE": { "is": [ - "animal", "artificiel", "inerte", "solide" ], "isnot": [ + "animal", "gazeux", "liquide", "naturel", @@ -941,11 +958,11 @@ }, "CHAUSSETTE": { "is": [ - "inerte", - "végétal" + "inerte" ], "isnot": [ - "animal" + "animal", + "végétal" ], "na": [] }, @@ -1378,11 +1395,11 @@ }, "FROMAGE": { "is": [ - "animal", "artificiel", "inerte" ], "isnot": [ + "animal", "liquide", "naturel", "végétal" @@ -1767,10 +1784,10 @@ }, "LUNETTES": { "is": [ - "animal", "inerte" ], "isnot": [ + "animal", "végétal" ], "na": [] @@ -1961,10 +1978,10 @@ }, "OREILLER": { "is": [ - "animal", "inerte" ], "isnot": [ + "animal", "végétal" ], "na": [] @@ -1987,11 +2004,11 @@ }, "PAIN": { "is": [ - "inerte", - "végétal" + "inerte" ], "isnot": [ - "animal" + "animal", + "végétal" ], "na": [] }, @@ -2202,12 +2219,12 @@ }, "PISCINE": { "is": [ - "inerte", - "végétal" + "inerte" ], "isnot": [ "animal", - "gazeux" + "gazeux", + "végétal" ], "na": [] }, @@ -2242,12 +2259,12 @@ "PLAGE": { "is": [ "inerte", - "naturel", - "végétal" + "naturel" ], "isnot": [ "animal", - "artificiel" + "artificiel", + "végétal" ], "na": [] }, @@ -2513,10 +2530,10 @@ }, "ROBINET": { "is": [ - "animal", "inerte" ], "isnot": [ + "animal", "végétal" ], "na": [] @@ -2761,12 +2778,12 @@ "SOUS-MARIN": { "is": [ "artificiel", - "inerte", - "végétal" + "inerte" ], "isnot": [ "animal", - "naturel" + "naturel", + "végétal" ], "na": [] }, @@ -2817,12 +2834,12 @@ }, "TABOURET": { "is": [ - "animal", "artificiel", "inerte", "solide" ], "isnot": [ + "animal", "gazeux", "liquide", "naturel", @@ -2978,10 +2995,10 @@ }, "TOURNEVIS": { "is": [ - "animal", "inerte" ], "isnot": [ + "animal", "gazeux", "végétal" ], @@ -3107,12 +3124,12 @@ }, "VOILIER": { "is": [ - "animal", "artificiel", "inerte", "solide" ], "isnot": [ + "animal", "gazeux", "liquide", "naturel", diff --git a/scripts/manage_data.php b/scripts/manage_data.php index 525e57194be7e97d5c19527545a6e334a543e2bc..417d4513206ef1cc9a328eae89efd3c4a1fad186 100644 --- a/scripts/manage_data.php +++ b/scripts/manage_data.php @@ -106,8 +106,8 @@ if (!is_writable($jsonDataFile)) { die; } -$categories = (\array_key_exists('categories', $data) && \is_array($data['categories'])) ? $data['categories'] : [];; -$items = (\array_key_exists('items', $data) && \is_array($data['items'])) ? $data['items'] : [];; +$categories = (\array_key_exists('categories', $data) && \is_array($data['categories'])) ? $data['categories'] : []; +$items = (\array_key_exists('items', $data) && \is_array($data['items'])) ? $data['items'] : []; // Manage categories exclusions $exclusions = (\array_key_exists('exclusions', $data) && \is_array($data['exclusions'])) ? $data['exclusions'] : []; @@ -121,14 +121,18 @@ foreach ($exclusions as $exclusionSet) { $categories = array_clean($categories); $items = array_clean($items); +$themes = (\array_key_exists('themes', $data) && \is_array($data['themes'])) ? $data['themes'] : []; + $data['categories'] = $categories; $data['items'] = $items; $data['exclusions'] = $exclusions; +$data['themes'] = $themes; dump(''); dump('Found ' . \count($categories) . ' unique categories.'); dump('Found ' . \count($items) . ' unique items.'); dump('Found ' . \count($exclusions) . ' exclusions sets.'); +dump('Found ' . \count($themes) . ' themes.'); // Get/init mapping data $mapping = (\array_key_exists('mapping', $data) && \is_array($data['mapping'])) ? $data['mapping'] : []; @@ -174,6 +178,13 @@ function showExclusions($exclusions) } } +function showThemes($theme) +{ + foreach ($theme as $name => $categories) { + dump($name . ': ' . \join(', ', $categories)); + } +} + function showMappings($mappingItems) { $columnsWidths = [ @@ -337,14 +348,15 @@ while ($exitMainLoop === false) { $missing = find_missing_associations($mappingItems, $categories, $items); $menu = [ - '0: exit', + '0: save and exit', '', '1: show categories (' . \count($categories) . ' found)', '2: show items (' . \count($items) . ' found)', '3: show exclusions (' . \count($exclusions) . ' found)', - '4: show mappings (' . \count($mappingItems) . ' found)', + '4: show themes (' . \count($themes) . ' found)', + '5: show mappings (' . \count($mappingItems) . ' found)', '', - '5: complete mappings (' . \count($missing) . ' missing)', + '6: complete mappings (' . \count($missing) . ' missing)', ]; $answer = ask(\join("\n", $menu)); @@ -362,9 +374,12 @@ while ($exitMainLoop === false) { showExclusions($exclusions); break; case '4': - showMappings($mappingItems); + showThemes($themes); break; case '5': + showMappings($mappingItems); + break; + case '6': $data['mapping']['items'] = editMappings($mappingItems, $categories, $items, $exclusions); break; default: