From bcd6a446ab2af15c78f57d284273f0dc82c45b05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr>
Date: Tue, 14 May 2024 15:42:27 +0200
Subject: [PATCH] Normalize game architecture

---
 android/gradle.properties                     |   4 +-
 assets/translations/en.json                   |   8 +-
 assets/translations/fr.json                   |   8 +-
 assets/{icons => ui}/button_back.png          | Bin
 .../button_delete_saved_game.png              | Bin
 assets/{icons => ui}/button_resume_game.png   | Bin
 assets/{icons => ui}/button_start.png         | Bin
 assets/{icons => ui}/game_fail.png            | Bin
 assets/{icons => ui}/game_win.png             | Bin
 assets/{icons => ui}/placeholder.png          | Bin
 devtools_options.yaml                         |   3 +
 .../metadata/android/en-US/changelogs/32.txt  |   1 +
 .../metadata/android/fr-FR/changelogs/32.txt  |   1 +
 icons/build_game_icons.sh                     |  84 -------
 icons/skin_default.svg                        |   2 -
 lib/{utils => config}/color_theme.dart        |  30 +--
 lib/config/default_game_settings.dart         |  53 +++--
 lib/config/default_global_settings.dart       |  57 ++---
 lib/config/menu.dart                          |  17 +-
 lib/config/theme.dart                         |  10 +-
 lib/cubit/game_cubit.dart                     |  76 ++++---
 lib/cubit/game_state.dart                     |   4 -
 lib/cubit/nav_cubit.dart                      |  16 +-
 lib/cubit/settings_game_cubit.dart            |  38 ++--
 lib/cubit/settings_game_state.dart            |   4 -
 lib/cubit/settings_global_cubit.dart          |  38 ++--
 lib/cubit/settings_global_state.dart          |   4 -
 lib/main.dart                                 |  57 +++--
 .../{game_board.dart => game/board.dart}      |  30 +--
 lib/models/{game_cell.dart => game/cell.dart} |   4 +-
 lib/models/{ => game}/cell_location.dart      |   0
 lib/models/{ => game}/game.dart               | 156 +++++++------
 lib/models/{ => settings}/settings_game.dart  |  27 ++-
 .../{ => settings}/settings_global.dart       |  23 +-
 .../game_end.dart}                            |  19 +-
 .../game_top.dart}                            |   6 +-
 lib/ui/helpers/app_titles.dart                |  32 +++
 .../helpers/outlined_text_widget.dart         |  16 +-
 .../game.dart => layouts/game_layout.dart}    |  25 +--
 .../parameters_layout.dart}                   | 205 +++++++++---------
 lib/ui/painters/game_board_painter.dart       |  16 +-
 lib/ui/parameters/parameter_image.dart        |  38 ++++
 .../parameter_painter.dart                    |  43 ++--
 lib/ui/screens/game_page.dart                 |  22 --
 .../{about_page.dart => page_about.dart}      |   8 +-
 lib/ui/screens/page_game.dart                 |  24 ++
 ...{settings_page.dart => page_settings.dart} |  10 +-
 .../{widgets => }/settings/settings_form.dart |   2 +-
 lib/ui/{widgets => }/settings/theme_card.dart |   0
 lib/ui/skeleton.dart                          |   2 +-
 .../actions/button_delete_saved_game.dart     |  21 ++
 lib/ui/widgets/actions/button_game_quit.dart  |  21 ++
 .../actions/button_game_start_new.dart        |  34 +++
 .../actions/button_resume_saved_game.dart     |  21 ++
 .../{game_board.dart => game/board.dart}      |  30 +--
 lib/ui/widgets/global_app_bar.dart            |  15 +-
 lib/ui/widgets/header_app.dart                |  24 --
 lib/ui/widgets/helpers/app_titles.dart        |  17 --
 .../indicator_available_blocks.dart           |   4 +-
 .../indicators/indicator_moves_count.dart     |   4 +-
 .../widgets/indicators/indicator_score.dart   |   4 +-
 .../indicators/indicator_shuffle_button.dart  |   7 +-
 pubspec.lock                                  |  52 ++---
 pubspec.yaml                                  |  11 +-
 .../app/build_application_resources.sh        |   2 +-
 {icons => resources/app}/featureGraphic.svg   |   0
 {icons => resources/app}/icon.svg             |   0
 resources/build_resources.sh                  |   7 +
 resources/ui/build_ui_resources.sh            | 110 ++++++++++
 .../ui/images}/button_back.svg                |   0
 .../ui/images}/button_delete_saved_game.svg   |   0
 .../ui/images}/button_resume_game.svg         |   0
 .../ui/images}/button_start.svg               |   0
 {icons => resources/ui/images}/game_fail.svg  |   0
 {icons => resources/ui/images}/game_win.svg   |   0
 .../ui/images}/placeholder.svg                |   0
 76 files changed, 922 insertions(+), 685 deletions(-)
 rename assets/{icons => ui}/button_back.png (100%)
 rename assets/{icons => ui}/button_delete_saved_game.png (100%)
 rename assets/{icons => ui}/button_resume_game.png (100%)
 rename assets/{icons => ui}/button_start.png (100%)
 rename assets/{icons => ui}/game_fail.png (100%)
 rename assets/{icons => ui}/game_win.png (100%)
 rename assets/{icons => ui}/placeholder.png (100%)
 create mode 100644 devtools_options.yaml
 create mode 100644 fastlane/metadata/android/en-US/changelogs/32.txt
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/32.txt
 delete mode 100755 icons/build_game_icons.sh
 delete mode 100644 icons/skin_default.svg
 rename lib/{utils => config}/color_theme.dart (71%)
 rename lib/models/{game_board.dart => game/board.dart} (64%)
 rename lib/models/{game_cell.dart => game/cell.dart} (87%)
 rename lib/models/{ => game}/cell_location.dart (100%)
 rename lib/models/{ => game}/game.dart (71%)
 rename lib/models/{ => settings}/settings_game.dart (52%)
 rename lib/models/{ => settings}/settings_global.dart (58%)
 rename lib/ui/{widgets/game_bottom_buttons.dart => game/game_end.dart} (64%)
 rename lib/ui/{widgets/game_top_indicator.dart => game/game_top.dart} (90%)
 create mode 100644 lib/ui/helpers/app_titles.dart
 rename lib/ui/{widgets => }/helpers/outlined_text_widget.dart (69%)
 rename lib/ui/{widgets/game.dart => layouts/game_layout.dart} (53%)
 rename lib/ui/{widgets/parameters.dart => layouts/parameters_layout.dart} (51%)
 create mode 100644 lib/ui/parameters/parameter_image.dart
 rename lib/ui/{painters => parameters}/parameter_painter.dart (92%)
 delete mode 100644 lib/ui/screens/game_page.dart
 rename lib/ui/screens/{about_page.dart => page_about.dart} (86%)
 create mode 100644 lib/ui/screens/page_game.dart
 rename lib/ui/screens/{settings_page.dart => page_settings.dart} (66%)
 rename lib/ui/{widgets => }/settings/settings_form.dart (96%)
 rename lib/ui/{widgets => }/settings/theme_card.dart (100%)
 create mode 100644 lib/ui/widgets/actions/button_delete_saved_game.dart
 create mode 100644 lib/ui/widgets/actions/button_game_quit.dart
 create mode 100644 lib/ui/widgets/actions/button_game_start_new.dart
 create mode 100644 lib/ui/widgets/actions/button_resume_saved_game.dart
 rename lib/ui/widgets/{game_board.dart => game/board.dart} (81%)
 delete mode 100644 lib/ui/widgets/header_app.dart
 delete mode 100644 lib/ui/widgets/helpers/app_titles.dart
 rename icons/build_application_icons.sh => resources/app/build_application_resources.sh (98%)
 rename {icons => resources/app}/featureGraphic.svg (100%)
 rename {icons => resources/app}/icon.svg (100%)
 create mode 100755 resources/build_resources.sh
 create mode 100755 resources/ui/build_ui_resources.sh
 rename {icons => resources/ui/images}/button_back.svg (100%)
 rename {icons => resources/ui/images}/button_delete_saved_game.svg (100%)
 rename {icons => resources/ui/images}/button_resume_game.svg (100%)
 rename {icons => resources/ui/images}/button_start.svg (100%)
 rename {icons => resources/ui/images}/game_fail.svg (100%)
 rename {icons => resources/ui/images}/game_win.svg (100%)
 rename {icons => resources/ui/images}/placeholder.svg (100%)

diff --git a/android/gradle.properties b/android/gradle.properties
index 9742e8d..658a39b 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.31
-app.versionCode=31
+app.versionName=0.1.0
+app.versionCode=32
diff --git a/assets/translations/en.json b/assets/translations/en.json
index f434c73..6924de1 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -1,14 +1,12 @@
 {
   "app_name": "Jeweled",
 
-  "bottom_nav_game": "Game",
-  "bottom_nav_settings": "Settings",
-  "bottom_nav_about": "About",
-
   "settings_title": "Settings",
   "settings_label_theme": "Theme mode",
 
   "about_title": "About",
   "about_content": "Simple and classic Jeweled game.",
-  "about_version": "Version: {version}"
+  "about_version": "Version: {version}",
+
+  "": ""
 }
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index 0952f45..23adc4c 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -1,14 +1,12 @@
 {
   "app_name": "Jeweled",
 
-  "bottom_nav_game": "Jeu",
-  "bottom_nav_settings": "Réglages",
-  "bottom_nav_about": "Infos",
-
   "settings_title": "Réglages",
   "settings_label_theme": "Thème de couleurs",
 
   "about_title": "Informations",
   "about_content": "Jeweled, jeu simple et classique d'associations.",
-  "about_version": "Version : {version}"
+  "about_version": "Version : {version}",
+
+  "": ""
 }
diff --git a/assets/icons/button_back.png b/assets/ui/button_back.png
similarity index 100%
rename from assets/icons/button_back.png
rename to assets/ui/button_back.png
diff --git a/assets/icons/button_delete_saved_game.png b/assets/ui/button_delete_saved_game.png
similarity index 100%
rename from assets/icons/button_delete_saved_game.png
rename to assets/ui/button_delete_saved_game.png
diff --git a/assets/icons/button_resume_game.png b/assets/ui/button_resume_game.png
similarity index 100%
rename from assets/icons/button_resume_game.png
rename to assets/ui/button_resume_game.png
diff --git a/assets/icons/button_start.png b/assets/ui/button_start.png
similarity index 100%
rename from assets/icons/button_start.png
rename to assets/ui/button_start.png
diff --git a/assets/icons/game_fail.png b/assets/ui/game_fail.png
similarity index 100%
rename from assets/icons/game_fail.png
rename to assets/ui/game_fail.png
diff --git a/assets/icons/game_win.png b/assets/ui/game_win.png
similarity index 100%
rename from assets/icons/game_win.png
rename to assets/ui/game_win.png
diff --git a/assets/icons/placeholder.png b/assets/ui/placeholder.png
similarity index 100%
rename from assets/icons/placeholder.png
rename to assets/ui/placeholder.png
diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 0000000..fa0b357
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/fastlane/metadata/android/en-US/changelogs/32.txt b/fastlane/metadata/android/en-US/changelogs/32.txt
new file mode 100644
index 0000000..d4afd51
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/32.txt
@@ -0,0 +1 @@
+Improve/normalize game architecture.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/32.txt b/fastlane/metadata/android/fr-FR/changelogs/32.txt
new file mode 100644
index 0000000..6a9871a
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/32.txt
@@ -0,0 +1 @@
+Amélioration/normalisation de l'architecture du jeu.
diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh
deleted file mode 100755
index 57b8c13..0000000
--- a/icons/build_game_icons.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#! /bin/bash
-
-# Check dependencies
-command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; }
-command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; }
-command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
-
-CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
-BASE_DIR="$(dirname "${CURRENT_DIR}")"
-ASSETS_DIR="${BASE_DIR}/assets"
-
-OPTIPNG_OPTIONS="-preserve -quiet -o7"
-ICON_SIZE=192
-
-#######################################################
-
-# Game images
-AVAILABLE_GAME_IMAGES="
-  button_back
-  button_start
-  button_resume_game
-  button_delete_saved_game
-  game_fail
-  game_win
-  placeholder
-"
-
-#######################################################
-
-# optimize svg
-function optimize_svg() {
-  SOURCE="$1"
-
-  cp ${SOURCE} ${SOURCE}.tmp
-  scour \
-      --remove-descriptive-elements \
-      --enable-id-stripping \
-      --enable-viewboxing \
-      --enable-comment-stripping \
-      --nindent=4 \
-      --quiet \
-      -i ${SOURCE}.tmp \
-      -o ${SOURCE}
-  rm ${SOURCE}.tmp
-}
-
-# build icons
-function build_icon() {
-  SOURCE="$1"
-  TARGET="$2"
-
-  echo "Building ${TARGET}"
-
-  if [ ! -f "${SOURCE}" ]; then
-    echo "Missing file: ${SOURCE}"
-    exit 1
-  fi
-
-  optimize_svg "${SOURCE}"
-
-  inkscape \
-      --export-width=${ICON_SIZE} \
-      --export-height=${ICON_SIZE} \
-      --export-filename=${TARGET} \
-      ${SOURCE}
-
-  optipng ${OPTIPNG_OPTIONS} ${TARGET}
-}
-
-#######################################################
-
-# Create output folders
-mkdir -p ${ASSETS_DIR}/icons
-mkdir -p ${ASSETS_DIR}/skins
-
-# Delete existing generated images
-find ${ASSETS_DIR}/icons -type f -name "*.png" -delete
-find ${ASSETS_DIR}/skins -type f -name "*.png" -delete
-
-# build game images
-for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES}
-do
-  build_icon ${CURRENT_DIR}/${GAME_IMAGE}.svg ${ASSETS_DIR}/icons/${GAME_IMAGE}.png
-done
diff --git a/icons/skin_default.svg b/icons/skin_default.svg
deleted file mode 100644
index f7344d5..0000000
--- a/icons/skin_default.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="100" height="100" ry="0" fill="#be6ade" stroke="#000" stroke-width="2"/></svg>
diff --git a/lib/utils/color_theme.dart b/lib/config/color_theme.dart
similarity index 71%
rename from lib/utils/color_theme.dart
rename to lib/config/color_theme.dart
index 8636921..c2d0f8f 100644
--- a/lib/utils/color_theme.dart
+++ b/lib/config/color_theme.dart
@@ -1,12 +1,12 @@
 import 'package:flutter/material.dart';
 
 class ColorTheme {
-  static Map<int, List<int>> itemColors = {
+  static Map<String, List<int>> itemColors = {
     // legacy
     // 0:[0x9D9D9D,0xFFFFFF,0xBE2633,0xE06F8B,0x493C2B,0xA46422,0xEB8931,0xF7E26B,0x2F484E,0x44891A,0xA3CE27,0x1B2632,0x005784,0x31A2F2,0xB2DCEF,],
 
     // https://lospec.com/palette-list/gothic-bit
-    1: [
+    'gothic-bit': [
       0x0e0e12,
       0x1a1a24,
       0x333346,
@@ -18,7 +18,7 @@ class ColorTheme {
     ],
 
     // https://lospec.com/palette-list/sweethope
-    2: [
+    'sweethope': [
       0x615e85,
       0x9c8dc2,
       0xd9a3cd,
@@ -30,7 +30,7 @@ class ColorTheme {
     ],
 
     // https://lospec.com/palette-list/nostalgic-dreams
-    3: [
+    'nostalgic-dreams': [
       0xd9af80,
       0xb07972,
       0x524352,
@@ -42,7 +42,7 @@ class ColorTheme {
     ],
 
     // https://lospec.com/palette-list/arjibi8
-    4: [
+    'arjibi8': [
       0x8bc7bf,
       0x5796a1,
       0x524bb3,
@@ -71,9 +71,9 @@ class ColorTheme {
 
   static int defaultItemColor = 0x808080;
 
-  static int getColorsCount(final int skin) {
-    if (itemColors.containsKey(skin) && null != itemColors[skin]) {
-      List<int> skinColors = itemColors[skin] ?? [];
+  static int getColorsCount(String colorsTheme) {
+    if (itemColors.containsKey(colorsTheme) && null != itemColors[colorsTheme]) {
+      List<int> skinColors = itemColors[colorsTheme] ?? [];
 
       return skinColors.length;
     }
@@ -81,16 +81,18 @@ class ColorTheme {
     return 0;
   }
 
-  static int getColorCode(int? value, final int skin) {
-    if (value != null && itemColors.containsKey(skin) && null != itemColors[skin]) {
-      List<int> skinColors = itemColors[skin] ?? [];
-      return (skinColors[value % getColorsCount(skin)]) | 0xFF000000;
+  static int getColorCode(int? value, String colorsTheme) {
+    if (value != null &&
+        itemColors.containsKey(colorsTheme) &&
+        null != itemColors[colorsTheme]) {
+      List<int> skinColors = itemColors[colorsTheme] ?? [];
+      return (skinColors[value % getColorsCount(colorsTheme)]) | 0xFF000000;
     }
     return defaultItemColor | 0xFF000000;
   }
 
-  static Color getColor(int? value, final int skin) {
-    return Color(getColorCode(value, skin));
+  static Color getColor(int? value, String colorsTheme) {
+    return Color(getColorCode(value, colorsTheme));
   }
 
   static Color getBorderColor() {
diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart
index f8426a7..21f433f 100644
--- a/lib/config/default_game_settings.dart
+++ b/lib/config/default_game_settings.dart
@@ -1,42 +1,48 @@
-import 'package:jeweled/utils/tools.dart';
+import 'package:jeweled/utils/tools.dart'; 
 
 class DefaultGameSettings {
+  // available game parameters codes
+  static const String parameterCodeBoardSize = 'boardSize';
+  static const String parameterCodeColorsCount = 'colorsCount';
   static const List<String> availableParameters = [
-    'boardSize',
-    'colorsCount',
+    parameterCodeBoardSize,
+    parameterCodeColorsCount,
   ];
 
-  static const int boardSizeValueSmall = 6;
-  static const int boardSizeValueMedium = 10;
-  static const int boardSizeValueLarge = 14;
-  static const int boardSizeValueExtraLarge = 18;
-
-  static const int defaultBoardSizeValue = boardSizeValueMedium;
-  static const List<int> allowedBoardSizeValues = [
+  // board size: available values
+  static const String boardSizeValueSmall = '6';
+  static const String boardSizeValueMedium = '10';
+  static const String boardSizeValueLarge = '14';
+  static const String boardSizeValueExtraLarge = '18';
+  static const List<String> allowedBoardSizeValues = [
     boardSizeValueSmall,
     boardSizeValueMedium,
     boardSizeValueLarge,
     boardSizeValueExtraLarge,
   ];
-
-  static const int colorsCountValueLow = 5;
-  static const int colorsCountValueMedium = 6;
-  static const int colorsCountValueHigh = 7;
-  static const int colorsCountValueVeryHigh = 8;
-
-  static const int defaultColorsCountValue = colorsCountValueMedium;
-  static const List<int> allowedColorsCountValues = [
+  // board size: default value
+  static const String defaultBoardSizeValue = boardSizeValueMedium;
+
+  // colors count: available values
+  static const String colorsCountValueLow = '5';
+  static const String colorsCountValueMedium = '6';
+  static const String colorsCountValueHigh = '7';
+  static const String colorsCountValueVeryHigh = '8';
+  static const List<String> allowedColorsCountValues = [
     colorsCountValueLow,
     colorsCountValueMedium,
     colorsCountValueHigh,
     colorsCountValueVeryHigh,
   ];
+  // colors count: default value
+  static const String defaultColorsCountValue = colorsCountValueMedium;
 
-  static List<int> getAvailableValues(String parameterCode) {
+  // available values from parameter code
+  static List<String> getAvailableValues(String parameterCode) {
     switch (parameterCode) {
-      case 'boardSize':
+      case parameterCodeBoardSize:
         return DefaultGameSettings.allowedBoardSizeValues;
-      case 'colorsCount':
+      case parameterCodeColorsCount:
         return DefaultGameSettings.allowedColorsCountValues;
     }
 
@@ -44,5 +50,10 @@ class DefaultGameSettings {
     return [];
   }
 
+  // parameters displayed with assets (instead of painter)
+  static List<String> displayedWithAssets = [
+    //
+  ];
+
   static int blockMinimumCellsCount = 3;
 }
diff --git a/lib/config/default_global_settings.dart b/lib/config/default_global_settings.dart
index 28fac83..105f9b6 100644
--- a/lib/config/default_global_settings.dart
+++ b/lib/config/default_global_settings.dart
@@ -1,38 +1,39 @@
-import 'package:jeweled/utils/tools.dart';
+import 'package:jeweled/utils/tools.dart'; 
 
 class DefaultGlobalSettings {
+  // available global parameters codes
+  static const String parameterCodeColorsTheme = 'colorsTheme';
+  static const String parameterCodeGraphicsTheme = 'graphicTheme';
   static const List<String> availableParameters = [
-    'colorsTheme',
-    'graphicTheme',
+    parameterCodeColorsTheme,
+    parameterCodeGraphicsTheme,
   ];
 
-  static const int defaultColorsThemeValue = 1;
-  static const List<int> allowedColorsThemeValues = [
-    // 0, // 0x9D9D9D,0xFFFFFF,0xBE2633,0xE06F8B,0x493C2B,0xA46422,0xEB8931,0xF7E26B,0x2F484E,0x44891A,0xA3CE27,0x1B2632,0x005784,0x31A2F2,0xB2DCEF,// legacy
-    1, // 0x0e0e12,0x1a1a24,0x333346,0x535373,0x8080a4,0xa6a6bf,0xc1c1d2,0xe6e6ec, // https://lospec.com/palette-list/gothic-bit
-    2, // 0x615e85,0x9c8dc2,0xd9a3cd,0xebc3a7,0xe0e0dc,0xa3d1af,0x90b4de,0x717fb0, // https://lospec.com/palette-list/sweethope
-    3, // 0xd9af80,0xb07972,0x524352,0x686887,0x7f9bb0,0xbfd4b0,0x90b870,0x628c70, // https://lospec.com/palette-list/nostalgic-dreams
-    4, // 0x8bc7bf,0x5796a1,0x524bb3,0x471b6e,0x702782,0xb0455a,0xde8b6f,0xebd694, // https://lospec.com/palette-list/arjibi8
-    // 5, // 0x40263e,0x5979a6,0x84c2a3,0xefe8c3,0xefefef,0xcbc7d6,0xd06060,0x773971, // https://lospec.com/palette-list/kotomasho-8
-    // 6, // 0xf0f0eb,0xffff8f,0x7be098,0x849ad8,0xe8b382,0xd8828e,0xa776c1,0x545155, // https://lospec.com/palette-list/desatur8
-    // 7, // 0x211d38,0x2e2a4f,0x3b405e,0x60556e,0x9a6278,0xc7786f,0xcfa98a,0xcdd4a5, // https://lospec.com/palette-list/purplemorning8
-    // 8, // 0x332422,0xc95b40,0xff9b5e,0xfcdf76,0x4c2f7f,0x3a66ad,0x39cec2,0xfafff9, // https://lospec.com/palette-list/cold-war-8
-    // 9, // 0x111323,0x374566,0x50785d,0x8497b3,0xe8dcd8,0xcfb463,0xb35447,0x692e4b, // https://lospec.com/palette-list/low-8
+  static const String colorThemeGothicBit = 'gothic-bit';
+  static const String colorThemeSweethope = 'sweethope';
+  static const String colorThemeNostalgicDreams = 'nostalgic-dreams';
+  static const String colorThemeArjibi8 = 'arjibi8';
+  static const String defaultColorsThemeValue = colorThemeSweethope;
+  static const List<String> allowedColorsThemeValues = [
+    colorThemeGothicBit,
+    colorThemeSweethope,
+    colorThemeNostalgicDreams,
+    colorThemeArjibi8,
   ];
 
-  static const int graphicThemeSolidBackground = 0;
-  static const int graphicThemeGradientAndBorder = 1;
-  static const int graphicThemeEmojis = 2;
-  static const int graphicThemePatterns = 3;
-
-  static const int defaultGraphicThemeValue = graphicThemeSolidBackground;
-  static const List<int> allowedGraphicThemeValues = [
+  static const String graphicThemeSolidBackground = 'SolidBackground';
+  static const String graphicThemeGradientAndBorder = 'GradientAndBorder';
+  static const String graphicThemeEmojis = 'Emojis';
+  static const String graphicThemePatterns = 'Patterns';
+  static const List<String> allowedGraphicThemeValues = [
     graphicThemeSolidBackground,
     graphicThemeGradientAndBorder,
     graphicThemeEmojis,
     graphicThemePatterns,
   ];
 
+  static const String defaultGraphicThemeValue = graphicThemeSolidBackground;
+
   static const List<String> graphicThemeContentEmojiStrings = [
     '🍏',
     '🤍',
@@ -54,15 +55,21 @@ class DefaultGlobalSettings {
     '⧧',
   ];
 
-  static List<int> getAvailableValues(String parameterCode) {
+  // available values from parameter code
+  static List<String> getAvailableValues(String parameterCode) {
     switch (parameterCode) {
-      case 'colorsTheme':
+      case parameterCodeColorsTheme:
         return DefaultGlobalSettings.allowedColorsThemeValues;
-      case 'graphicTheme':
+      case parameterCodeGraphicsTheme:
         return DefaultGlobalSettings.allowedGraphicThemeValues;
     }
 
     printlog('Did not find any available value for global parameter "$parameterCode".');
     return [];
   }
+
+  // parameters displayed with assets (instead of painter)
+  static List<String> displayedWithAssets = [
+    //
+  ];
 }
diff --git a/lib/config/menu.dart b/lib/config/menu.dart
index 79af6d0..aa89aa5 100644
--- a/lib/config/menu.dart
+++ b/lib/config/menu.dart
@@ -1,17 +1,15 @@
 import 'package:flutter/material.dart';
 import 'package:unicons/unicons.dart';
 
-import 'package:jeweled/ui/screens/about_page.dart';
-import 'package:jeweled/ui/screens/game_page.dart';
-import 'package:jeweled/ui/screens/settings_page.dart';
+import 'package:jeweled/ui/screens/page_game.dart';
+import 'package:jeweled/ui/screens/page_settings.dart';
+import 'package:jeweled/ui/screens/page_about.dart';
 
 class MenuItem {
-  final String code;
   final Icon icon;
   final Widget page;
 
   const MenuItem({
-    required this.code,
     required this.icon,
     required this.page,
   });
@@ -20,23 +18,20 @@ class MenuItem {
 class Menu {
   static const indexGame = 0;
   static const menuItemGame = MenuItem(
-    code: 'bottom_nav_game',
     icon: Icon(UniconsLine.home),
-    page: GamePage(),
+    page: PageGame(),
   );
 
   static const indexSettings = 1;
   static const menuItemSettings = MenuItem(
-    code: 'bottom_nav_settings',
     icon: Icon(UniconsLine.setting),
-    page: SettingsPage(),
+    page: PageSettings(),
   );
 
   static const indexAbout = 2;
   static const menuItemAbout = MenuItem(
-    code: 'bottom_nav_about',
     icon: Icon(UniconsLine.info_circle),
-    page: AboutPage(),
+    page: PageAbout(),
   );
 
   static Map<int, MenuItem> items = {
diff --git a/lib/config/theme.dart b/lib/config/theme.dart
index be39034..74f532f 100644
--- a/lib/config/theme.dart
+++ b/lib/config/theme.dart
@@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light(
   secondary: primarySwatch.shade500,
   onSecondary: Colors.white,
   error: errorColor,
-  background: textSwatch.shade200,
-  onBackground: textSwatch.shade500,
   onSurface: textSwatch.shade500,
   surface: textSwatch.shade50,
-  surfaceVariant: Colors.white,
+  surfaceContainerHighest: Colors.white,
   shadow: textSwatch.shade900.withOpacity(.1),
 );
 
@@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark(
   secondary: primarySwatch.shade500,
   onSecondary: Colors.white,
   error: errorColor,
-  background: const Color(0xFF171724),
-  onBackground: textSwatch.shade400,
   onSurface: textSwatch.shade300,
   surface: const Color(0xFF262630),
-  surfaceVariant: const Color(0xFF282832),
+  surfaceContainerHighest: const Color(0xFF282832),
   shadow: textSwatch.shade900.withOpacity(.2),
 );
 
@@ -192,5 +188,3 @@ final ThemeData darkTheme = lightTheme.copyWith(
     ),
   ),
 );
-
-final ThemeData appTheme = darkTheme;
diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart
index a0b81fd..276c945 100644
--- a/lib/cubit/game_cubit.dart
+++ b/lib/cubit/game_cubit.dart
@@ -3,10 +3,10 @@ import 'package:flutter/material.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
 import 'package:jeweled/config/default_game_settings.dart';
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/models/cell_location.dart';
-import 'package:jeweled/models/settings_game.dart';
-import 'package:jeweled/models/settings_global.dart';
+import 'package:jeweled/models/game/cell_location.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/models/settings/settings_game.dart';
+import 'package:jeweled/models/settings/settings_global.dart';
 import 'package:jeweled/utils/tools.dart';
 
 part 'game_state.dart';
@@ -14,7 +14,7 @@ part 'game_state.dart';
 class GameCubit extends HydratedCubit<GameState> {
   GameCubit()
       : super(GameState(
-          currentGame: Game.createNull(),
+          currentGame: Game.createEmpty(),
         ));
 
   void updateState(Game game) {
@@ -25,23 +25,55 @@ class GameCubit extends HydratedCubit<GameState> {
 
   void refresh() {
     final Game game = Game(
-      board: state.currentGame.board,
+      // Settings
       gameSettings: state.currentGame.gameSettings,
       globalSettings: state.currentGame.globalSettings,
-      shuffledColors: state.currentGame.shuffledColors,
+      // State
       isRunning: state.currentGame.isRunning,
+      isStarted: state.currentGame.isStarted,
       isFinished: state.currentGame.isFinished,
+      animationInProgress: state.currentGame.animationInProgress,
+      // Base data
+      board: state.currentGame.board,
+      // Game data
+      shuffledColors: state.currentGame.shuffledColors,
       availableBlocksCount: state.currentGame.availableBlocksCount,
-      movesCount: state.currentGame.movesCount,
       score: state.currentGame.score,
+      movesCount: state.currentGame.movesCount,
     );
     // game.dump();
 
     updateState(game);
   }
 
+  void startNewGame({
+    required GameSettings gameSettings,
+    required GlobalSettings globalSettings,
+  }) {
+    final Game newGame = Game.createNew(
+      gameSettings: gameSettings,
+      globalSettings: globalSettings,
+    );
+
+    newGame.dump();
+
+    updateState(newGame);
+    postAnimate();
+  }
+
   void quitGame() {
-    state.currentGame.updateGameIsRunning(false);
+    state.currentGame.isRunning = false;
+    refresh();
+  }
+
+  void resumeSavedGame() {
+    state.currentGame.isRunning = true;
+    refresh();
+  }
+
+  void deleteSavedGame() {
+    state.currentGame.isRunning = false;
+    state.currentGame.isFinished = true;
     refresh();
   }
 
@@ -51,6 +83,7 @@ class GameCubit extends HydratedCubit<GameState> {
   }
 
   void increaseMovesCount() {
+    state.currentGame.isStarted = true;
     state.currentGame.increaseMovesCount();
     refresh();
   }
@@ -66,19 +99,19 @@ class GameCubit extends HydratedCubit<GameState> {
   }
 
   void updateGameIsFinished(bool gameIsFinished) {
-    state.currentGame.updateGameIsFinished(gameIsFinished);
+    state.currentGame.isFinished = gameIsFinished;
     refresh();
   }
 
-  void shuffleColors(final int colorsTheme) {
+  void shuffleColors(final String colorsTheme) {
     state.currentGame.shuffleColorsAgain(colorsTheme);
   }
 
   moveCellsDown() {
     final Game currentGame = state.currentGame;
 
-    final int boardSizeHorizontal = currentGame.gameSettings.boardSize;
-    final int boardSizeVertical = currentGame.gameSettings.boardSize;
+    final int boardSizeHorizontal = currentGame.gameSettings.boardSizeValue;
+    final int boardSizeVertical = currentGame.gameSettings.boardSizeValue;
 
     for (int row = 0; row < boardSizeVertical; row++) {
       for (int col = 0; col < boardSizeHorizontal; col++) {
@@ -139,24 +172,9 @@ class GameCubit extends HydratedCubit<GameState> {
     }
   }
 
-  void startNewGame({
-    required GameSettings gameSettings,
-    required GlobalSettings globalSettings,
-  }) {
-    Game newGame = Game.createNew(
-      gameSettings: gameSettings,
-      globalSettings: globalSettings,
-    );
-
-    newGame.dump();
-
-    updateState(newGame);
-    postAnimate();
-  }
-
   @override
   GameState? fromJson(Map<String, dynamic> json) {
-    Game currentGame = json['currentGame'] as Game;
+    final Game currentGame = json['currentGame'] as Game;
 
     return GameState(
       currentGame: currentGame,
diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart
index 3fd161a..00e2116 100644
--- a/lib/cubit/game_state.dart
+++ b/lib/cubit/game_state.dart
@@ -12,8 +12,4 @@ class GameState extends Equatable {
   List<dynamic> get props => <dynamic>[
         currentGame,
       ];
-
-  Map<String, dynamic> get values => <String, dynamic>{
-        'currentGame': currentGame,
-      };
 }
diff --git a/lib/cubit/nav_cubit.dart b/lib/cubit/nav_cubit.dart
index f2f4628..47db0b1 100644
--- a/lib/cubit/nav_cubit.dart
+++ b/lib/cubit/nav_cubit.dart
@@ -17,20 +17,12 @@ class NavCubit extends HydratedCubit<int> {
     emit(Menu.indexGame);
   }
 
-  void switchToSettingsPage() {
-    if (state != Menu.indexSettings) {
-      emit(Menu.indexSettings);
-    } else {
-      goToGamePage();
-    }
+  void goToSettingsPage() {
+    emit(Menu.indexSettings);
   }
 
-  void switchToAboutPage() {
-    if (state != Menu.indexAbout) {
-      emit(Menu.indexAbout);
-    } else {
-      goToGamePage();
-    }
+  void goToAboutPage() {
+    emit(Menu.indexAbout);
   }
 
   @override
diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart
index 0aa8b0a..53df322 100644
--- a/lib/cubit/settings_game_cubit.dart
+++ b/lib/cubit/settings_game_cubit.dart
@@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:jeweled/models/settings_game.dart';
-import 'package:jeweled/utils/tools.dart';
+import 'package:jeweled/config/default_game_settings.dart';
+import 'package:jeweled/models/settings/settings_game.dart';
 
 part 'settings_game_state.dart';
 
@@ -11,8 +11,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> {
   GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault()));
 
   void setValues({
-    int? boardSize,
-    int? colorsCount,
+    String? boardSize,
+    String? colorsCount,
   }) {
     emit(
       GameSettingsState(
@@ -24,22 +24,24 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> {
     );
   }
 
-  int getParameterValue(String code) {
+  String getParameterValue(String code) {
     switch (code) {
-      case 'boardSize':
+      case DefaultGameSettings.parameterCodeBoardSize:
         return GameSettings.getBoardSizeValueFromUnsafe(state.settings.boardSize);
-      case 'colorsCount':
+      case DefaultGameSettings.parameterCodeColorsCount:
         return GameSettings.getColorsCountValueFromUnsafe(state.settings.colorsCount);
     }
-    return 0;
-  }
 
-  void setParameterValue(String code, int value) {
-    printlog('GameSettingsCubit.setParameterValue');
-    printlog('code: $code  / value: $value');
+    return '';
+  }
 
-    int boardSize = code == 'boardSize' ? value : getParameterValue('boardSize');
-    int colorsCount = code == 'colorsCount' ? value : getParameterValue('colorsCount');
+  void setParameterValue(String code, String value) {
+    final String boardSize = code == DefaultGameSettings.parameterCodeBoardSize
+        ? value
+        : getParameterValue(DefaultGameSettings.parameterCodeBoardSize);
+    final String colorsCount = code == DefaultGameSettings.parameterCodeColorsCount
+        ? value
+        : getParameterValue(DefaultGameSettings.parameterCodeColorsCount);
 
     setValues(
       boardSize: boardSize,
@@ -49,8 +51,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> {
 
   @override
   GameSettingsState? fromJson(Map<String, dynamic> json) {
-    int boardSize = json['boardSize'] as int;
-    int colorsCount = json['colorsCount'] as int;
+    final String boardSize = json[DefaultGameSettings.parameterCodeBoardSize] as String;
+    final String colorsCount = json[DefaultGameSettings.parameterCodeColorsCount] as String;
 
     return GameSettingsState(
       settings: GameSettings(
@@ -63,8 +65,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> {
   @override
   Map<String, dynamic>? toJson(GameSettingsState state) {
     return <String, dynamic>{
-      'boardSize': state.settings.boardSize,
-      'colorsCount': state.settings.colorsCount,
+      DefaultGameSettings.parameterCodeBoardSize: state.settings.boardSize,
+      DefaultGameSettings.parameterCodeColorsCount: state.settings.colorsCount,
     };
   }
 }
diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart
index b773dc6..5acd85b 100644
--- a/lib/cubit/settings_game_state.dart
+++ b/lib/cubit/settings_game_state.dart
@@ -12,8 +12,4 @@ class GameSettingsState extends Equatable {
   List<dynamic> get props => <dynamic>[
         settings,
       ];
-
-  Map<String, dynamic> get values => <String, dynamic>{
-        'settings': settings,
-      };
 }
diff --git a/lib/cubit/settings_global_cubit.dart b/lib/cubit/settings_global_cubit.dart
index 1813642..eadc407 100644
--- a/lib/cubit/settings_global_cubit.dart
+++ b/lib/cubit/settings_global_cubit.dart
@@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:jeweled/models/settings_global.dart';
-import 'package:jeweled/utils/tools.dart';
+import 'package:jeweled/config/default_global_settings.dart';
+import 'package:jeweled/models/settings/settings_global.dart';
 
 part 'settings_global_state.dart';
 
@@ -11,8 +11,8 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
   GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault()));
 
   void setValues({
-    int? colorsTheme,
-    int? graphicTheme,
+    String? colorsTheme,
+    String? graphicTheme,
   }) {
     emit(
       GlobalSettingsState(
@@ -24,22 +24,23 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
     );
   }
 
-  int getParameterValue(String code) {
+  String getParameterValue(String code) {
     switch (code) {
-      case 'colorsTheme':
+      case DefaultGlobalSettings.parameterCodeColorsTheme:
         return GlobalSettings.getColorsThemeValueFromUnsafe(state.settings.colorsTheme);
-      case 'graphicTheme':
+      case DefaultGlobalSettings.parameterCodeGraphicsTheme:
         return GlobalSettings.getGraphicThemeValueFromUnsafe(state.settings.graphicTheme);
     }
-    return 0;
+    return '';
   }
 
-  void setParameterValue(String code, int value) {
-    printlog('GlobalSettingsCubit.setParameterValue');
-    printlog('code: $code  / value: $value');
-
-    int colorsTheme = code == 'colorsTheme' ? value : getParameterValue('colorsTheme');
-    int graphicTheme = code == 'graphicTheme' ? value : getParameterValue('graphicTheme');
+  void setParameterValue(String code, String value) {
+    final String colorsTheme = code == DefaultGlobalSettings.parameterCodeColorsTheme
+        ? value
+        : getParameterValue(DefaultGlobalSettings.parameterCodeColorsTheme);
+    final String graphicTheme = code == DefaultGlobalSettings.parameterCodeGraphicsTheme
+        ? value
+        : getParameterValue(DefaultGlobalSettings.parameterCodeGraphicsTheme);
 
     setValues(
       colorsTheme: colorsTheme,
@@ -49,8 +50,9 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
 
   @override
   GlobalSettingsState? fromJson(Map<String, dynamic> json) {
-    int colorsTheme = json['colorsTheme'] as int;
-    int graphicTheme = json['graphicTheme'] as int;
+    final String colorsTheme = json[DefaultGlobalSettings.parameterCodeColorsTheme] as String;
+    final String graphicTheme =
+        json[DefaultGlobalSettings.parameterCodeGraphicsTheme] as String;
 
     return GlobalSettingsState(
       settings: GlobalSettings(
@@ -63,8 +65,8 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
   @override
   Map<String, dynamic>? toJson(GlobalSettingsState state) {
     return <String, dynamic>{
-      'colorsTheme': state.settings.colorsTheme,
-      'graphicTheme': state.settings.graphicTheme,
+      DefaultGlobalSettings.parameterCodeColorsTheme: state.settings.colorsTheme,
+      DefaultGlobalSettings.parameterCodeGraphicsTheme: state.settings.graphicTheme,
     };
   }
 }
diff --git a/lib/cubit/settings_global_state.dart b/lib/cubit/settings_global_state.dart
index 4e4fbdf..ebcddd7 100644
--- a/lib/cubit/settings_global_state.dart
+++ b/lib/cubit/settings_global_state.dart
@@ -12,8 +12,4 @@ class GlobalSettingsState extends Equatable {
   List<dynamic> get props => <dynamic>[
         settings,
       ];
-
-  Map<String, dynamic> get values => <String, dynamic>{
-        'settings': settings,
-      };
 }
diff --git a/lib/main.dart b/lib/main.dart
index 11da372..f62d4d5 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -2,18 +2,19 @@ import 'dart:io';
 
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hive/hive.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
+import 'package:jeweled/cubit/nav_cubit.dart';
+import 'package:jeweled/cubit/settings_game_cubit.dart';
+import 'package:jeweled/cubit/settings_global_cubit.dart';
+import 'package:jeweled/ui/skeleton.dart';
 import 'package:path_provider/path_provider.dart';
 
 import 'package:jeweled/config/theme.dart';
 import 'package:jeweled/cubit/game_cubit.dart';
-import 'package:jeweled/cubit/nav_cubit.dart';
-import 'package:jeweled/cubit/settings_game_cubit.dart';
-import 'package:jeweled/cubit/settings_global_cubit.dart';
 import 'package:jeweled/cubit/theme_cubit.dart';
-import 'package:jeweled/ui/skeleton.dart';
 
 void main() async {
   // Initialize packages
@@ -25,18 +26,17 @@ void main() async {
     storageDirectory: tmpDir,
   );
 
-  runApp(
-    EasyLocalization(
-      path: 'assets/translations',
-      supportedLocales: const <Locale>[
-        Locale('en'),
-        Locale('fr'),
-      ],
-      fallbackLocale: const Locale('en'),
-      useFallbackTranslations: true,
-      child: const MyApp(),
-    ),
-  );
+  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
+      .then((value) => runApp(EasyLocalization(
+            path: 'assets/translations',
+            supportedLocales: const <Locale>[
+              Locale('en'),
+              Locale('fr'),
+            ],
+            fallbackLocale: const Locale('en'),
+            useFallbackTranslations: true,
+            child: const MyApp(),
+          )));
 }
 
 class MyApp extends StatelessWidget {
@@ -44,6 +44,11 @@ class MyApp extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
+    final List<String> assets = getImagesAssets();
+    for (String asset in assets) {
+      precacheImage(AssetImage(asset), context);
+    }
+
     return MultiBlocProvider(
       providers: [
         BlocProvider<NavCubit>(create: (context) => NavCubit()),
@@ -73,4 +78,24 @@ class MyApp extends StatelessWidget {
       ),
     );
   }
+
+  List<String> getImagesAssets() {
+    final List<String> assets = [];
+
+    final List<String> gameImages = [
+      'button_back',
+      'button_delete_saved_game',
+      'button_resume_game',
+      'button_start',
+      'game_fail',
+      'game_win',
+      'placeholder',
+    ];
+
+    for (String image in gameImages) {
+      assets.add('assets/ui/$image.png');
+    }
+
+    return assets;
+  }
 }
diff --git a/lib/models/game_board.dart b/lib/models/game/board.dart
similarity index 64%
rename from lib/models/game_board.dart
rename to lib/models/game/board.dart
index f30f306..50db4a0 100644
--- a/lib/models/game_board.dart
+++ b/lib/models/game/board.dart
@@ -1,38 +1,38 @@
 import 'dart:math';
 
-import 'package:jeweled/models/game_cell.dart';
-import 'package:jeweled/models/settings_game.dart';
+import 'package:jeweled/models/game/cell.dart';
+import 'package:jeweled/models/settings/settings_game.dart';
 import 'package:jeweled/utils/tools.dart';
 
-class GameBoard {
-  final List<List<GameCell>> cells;
+class Board {
+  final List<List<Cell>> cells;
 
-  GameBoard({
+  Board({
     required this.cells,
   });
 
-  factory GameBoard.createNull() {
-    return GameBoard(cells: []);
+  factory Board.createEmpty() {
+    return Board(cells: []);
   }
 
-  factory GameBoard.createRandom(GameSettings gameSettings) {
-    final int boardSizeHorizontal = gameSettings.boardSize;
-    final int boardSizeVertical = gameSettings.boardSize;
-    final int maxValue = gameSettings.colorsCount;
+  factory Board.createRandom(GameSettings gameSettings) {
+    final int boardSizeHorizontal = gameSettings.boardSizeValue;
+    final int boardSizeVertical = gameSettings.boardSizeValue;
+    final int maxValue = gameSettings.colorsCountValue;
 
     final rand = Random();
 
-    List<List<GameCell>> cells = [];
+    List<List<Cell>> cells = [];
     for (int rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
-      List<GameCell> row = [];
+      List<Cell> row = [];
       for (int colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
         int value = 1 + rand.nextInt(maxValue);
-        row.add(GameCell(value));
+        row.add(Cell(value));
       }
       cells.add(row);
     }
 
-    return GameBoard(
+    return Board(
       cells: cells,
     );
   }
diff --git a/lib/models/game_cell.dart b/lib/models/game/cell.dart
similarity index 87%
rename from lib/models/game_cell.dart
rename to lib/models/game/cell.dart
index 4cba17b..8c28528 100644
--- a/lib/models/game_cell.dart
+++ b/lib/models/game/cell.dart
@@ -1,7 +1,7 @@
-class GameCell {
+class Cell {
   int? value;
 
-  GameCell(
+  Cell(
     this.value,
   );
 
diff --git a/lib/models/cell_location.dart b/lib/models/game/cell_location.dart
similarity index 100%
rename from lib/models/cell_location.dart
rename to lib/models/game/cell_location.dart
diff --git a/lib/models/game.dart b/lib/models/game/game.dart
similarity index 71%
rename from lib/models/game.dart
rename to lib/models/game/game.dart
index c302b35..6c4f4ae 100644
--- a/lib/models/game.dart
+++ b/lib/models/game/game.dart
@@ -1,42 +1,63 @@
 import 'dart:math';
 
+import 'package:jeweled/config/color_theme.dart';
 import 'package:jeweled/config/default_global_settings.dart';
-import 'package:jeweled/models/game_board.dart';
-import 'package:jeweled/models/game_cell.dart';
-import 'package:jeweled/models/cell_location.dart';
-import 'package:jeweled/models/settings_game.dart';
-import 'package:jeweled/models/settings_global.dart';
-import 'package:jeweled/utils/color_theme.dart';
+import 'package:jeweled/models/game/board.dart';
+import 'package:jeweled/models/game/cell.dart';
+import 'package:jeweled/models/game/cell_location.dart';
+import 'package:jeweled/models/settings/settings_game.dart';
+import 'package:jeweled/models/settings/settings_global.dart';
 import 'package:jeweled/utils/tools.dart';
 
 class Game {
-  final GameBoard board;
-  final GameSettings gameSettings;
-  final GlobalSettings globalSettings;
-  List<int> shuffledColors = [];
-  bool isRunning = false;
-  bool isFinished = false;
-  int availableBlocksCount = 0;
-  int movesCount = 0;
-  int score = 0;
-
   Game({
-    required this.board,
+    // Settings
     required this.gameSettings,
     required this.globalSettings,
-    required this.shuffledColors,
+
+    // State
     this.isRunning = false,
+    this.isStarted = false,
     this.isFinished = false,
+    this.animationInProgress = false,
+
+    // Base data
+    required this.board,
+
+    // Game data
+    required this.shuffledColors,
     this.availableBlocksCount = 0,
-    this.movesCount = 0,
     this.score = 0,
+    this.movesCount = 0,
   });
 
-  factory Game.createNull() {
+  // Settings
+  final GameSettings gameSettings;
+  final GlobalSettings globalSettings;
+
+  // State
+  bool isRunning;
+  bool isStarted;
+  bool isFinished;
+  bool animationInProgress;
+
+  // Base data
+  final Board board;
+
+  // Game data
+  List<int> shuffledColors;
+  int availableBlocksCount;
+  int score;
+  int movesCount;
+
+  factory Game.createEmpty() {
     return Game(
-      board: GameBoard.createNull(),
+      // Settings
       gameSettings: GameSettings.createDefault(),
       globalSettings: GlobalSettings.createDefault(),
+      // Base data
+      board: Board.createEmpty(),
+      // Game data
       shuffledColors: shuffleColors(DefaultGlobalSettings.defaultColorsThemeValue),
     );
   }
@@ -45,19 +66,27 @@ class Game {
     GameSettings? gameSettings,
     GlobalSettings? globalSettings,
   }) {
-    GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
-    GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
+    final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
+    final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
 
     return Game(
-      board: GameBoard.createRandom(newGameSettings),
+      // Settings
       gameSettings: newGameSettings,
       globalSettings: newGlobalSettings,
-      shuffledColors: shuffleColors(newGlobalSettings.colorsTheme),
+      // State
       isRunning: true,
+      // Base data
+      board: Board.createRandom(newGameSettings),
+      // Game data
+      shuffledColors: shuffleColors(newGlobalSettings.colorsTheme),
     );
   }
 
-  static List<int> shuffleColors(final int colorsTheme) {
+  bool get canBeResumed => isStarted && !isFinished;
+
+  bool get gameWon => isRunning && isStarted && isFinished;
+
+  static List<int> shuffleColors(final String colorsTheme) {
     List<int> values =
         List<int>.generate(ColorTheme.getColorsCount(colorsTheme), (i) => i + 1);
     values.shuffle();
@@ -65,11 +94,11 @@ class Game {
     return values;
   }
 
-  void shuffleColorsAgain(final int colorsTheme) {
+  void shuffleColorsAgain(final String colorsTheme) {
     shuffledColors = shuffleColors(colorsTheme);
   }
 
-  GameCell getCell(CellLocation cellLocation) {
+  Cell getCell(CellLocation cellLocation) {
     return board.cells[cellLocation.row][cellLocation.col];
   }
 
@@ -98,20 +127,12 @@ class Game {
     availableBlocksCount = getAvailableBlocks(this).length;
   }
 
-  void updateGameIsRunning(bool gameIsRunning) {
-    isRunning = gameIsRunning;
-  }
-
-  void updateGameIsFinished(bool gameIsFinished) {
-    isFinished = gameIsFinished;
-  }
-
   List<CellLocation> getSiblingCells(
     final CellLocation referenceCellLocation,
     List<CellLocation> siblingCells,
   ) {
-    final int boardSizeHorizontal = gameSettings.boardSize;
-    final int boardSizeVertical = gameSettings.boardSize;
+    final int boardSizeHorizontal = gameSettings.boardSizeValue;
+    final int boardSizeVertical = gameSettings.boardSizeValue;
 
     final int? referenceValue = getCellValue(referenceCellLocation);
 
@@ -147,8 +168,8 @@ class Game {
   }
 
   List<List<CellLocation>> getAvailableBlocks(final Game game) {
-    final int boardSizeHorizontal = game.gameSettings.boardSize;
-    final int boardSizeVertical = game.gameSettings.boardSize;
+    final int boardSizeHorizontal = game.gameSettings.boardSizeValue;
+    final int boardSizeVertical = game.gameSettings.boardSizeValue;
 
     final List<List<CellLocation>> blocks = [];
 
@@ -180,8 +201,8 @@ class Game {
   }
 
   bool hasAtLeastOneAvailableBlock() {
-    final int boardSizeHorizontal = gameSettings.boardSize;
-    final int boardSizeVertical = gameSettings.boardSize;
+    final int boardSizeHorizontal = gameSettings.boardSizeValue;
+    final int boardSizeVertical = gameSettings.boardSizeValue;
 
     for (int row = 0; row < boardSizeVertical; row++) {
       for (int col = 0; col < boardSizeHorizontal; col++) {
@@ -201,10 +222,9 @@ class Game {
   }
 
   bool isInBoard(CellLocation cell) {
-    if (cell.row > 0 &&
-        cell.row < gameSettings.boardSize &&
-        cell.col > 0 &&
-        cell.col < gameSettings.boardSize) {
+    final int boardSize = gameSettings.boardSizeValue;
+
+    if (cell.row > 0 && cell.row < boardSize && cell.col > 0 && cell.col < boardSize) {
       return true;
     }
     return false;
@@ -218,14 +238,14 @@ class Game {
     final List<int> values = [];
 
     // All eligible values (twice)
-    final int maxValue = gameSettings.colorsCount;
+    final int maxValue = gameSettings.colorsCountValue;
     for (int i = 1; i <= maxValue; i++) {
       values.add(i);
       values.add(i);
     }
 
     // Add values of current col (twice)
-    for (int r = 0; r <= gameSettings.boardSize; r++) {
+    for (int r = 0; r <= gameSettings.boardSizeValue; r++) {
       if (isInBoard(CellLocation.go(r, col))) {
         final int? value = getCellValue(CellLocation.go(r, col));
         if (value != null) {
@@ -238,12 +258,12 @@ class Game {
     // Add values of sibling cols (twice for top rows)
     for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
       final int c = col + deltaCol;
-      for (int r = 0; r < gameSettings.boardSize; r++) {
+      for (int r = 0; r < gameSettings.boardSizeValue; r++) {
         if (isInBoard(CellLocation.go(r, c))) {
           final int? value = getCellValue(CellLocation.go(r, c));
           if (value != null) {
             values.add(value);
-            if (row < gameSettings.boardSize / 3) {
+            if (row < gameSettings.boardSizeValue / 3) {
               values.add(value);
             }
           }
@@ -273,37 +293,47 @@ class Game {
     printlog('');
     printlog('## Current game dump:');
     printlog('');
+    printlog('$Game:');
+    printlog('  Settings');
     gameSettings.dump();
     globalSettings.dump();
-    printlog('');
+    printlog('  State');
+    printlog('    isRunning: $isRunning');
+    printlog('    isStarted: $isStarted');
+    printlog('    isFinished: $isFinished');
+    printlog('    animationInProgress: $animationInProgress');
+    printlog('  Base data');
     board.dump();
-    printlog('');
-    printlog('Game: ');
-    printlog('  isRunning: $isRunning');
-    printlog('  isFinished: $isFinished');
-    printlog('  movesCount: $movesCount');
-    printlog('  score: $score');
-    printlog('  availableBlocksCount: $availableBlocksCount');
-    printlog('  shuffledColors: $shuffledColors');
+    printlog('  Game data');
+    printlog('    shuffledColors: $shuffledColors');
+    printlog('    availableBlocksCount: $availableBlocksCount');
+    printlog('    score: $score');
+    printlog('    movesCount: $movesCount');
     printlog('');
   }
 
   @override
   String toString() {
-    return 'Game(${toJson()})';
+    return '$Game(${toJson()})';
   }
 
   Map<String, dynamic>? toJson() {
     return <String, dynamic>{
-      'board': board.toJson(),
+      // Settings
       'gameSettings': gameSettings.toJson(),
       'globalSettings': globalSettings.toJson(),
-      'shuffledColors': shuffledColors,
+      // State
       'isRunning': isRunning,
+      'isStarted': isStarted,
       'isFinished': isFinished,
+      'animationInProgress': animationInProgress,
+      // Base data
+      'board': board.toJson(),
+      // Game data
+      'shuffledColors': shuffledColors,
       'availableBlocksCount': availableBlocksCount,
-      'movesCount': movesCount,
       'score': score,
+      'movesCount': movesCount,
     };
   }
 }
diff --git a/lib/models/settings_game.dart b/lib/models/settings/settings_game.dart
similarity index 52%
rename from lib/models/settings_game.dart
rename to lib/models/settings/settings_game.dart
index 39cdebf..da6f4e3 100644
--- a/lib/models/settings_game.dart
+++ b/lib/models/settings/settings_game.dart
@@ -1,16 +1,20 @@
 import 'package:jeweled/config/default_game_settings.dart';
-import 'package:jeweled/utils/tools.dart';
+import 'package:jeweled/utils/tools.dart'; 
 
 class GameSettings {
-  final int boardSize;
-  final int colorsCount;
+  final String boardSize;
+  final String colorsCount;
 
   GameSettings({
     required this.boardSize,
     required this.colorsCount,
   });
 
-  static int getBoardSizeValueFromUnsafe(int size) {
+  // Getters to convert String to int
+  int get boardSizeValue => int.parse(boardSize);
+  int get colorsCountValue => int.parse(colorsCount);
+
+  static String getBoardSizeValueFromUnsafe(String size) {
     if (DefaultGameSettings.allowedBoardSizeValues.contains(size)) {
       return size;
     }
@@ -18,7 +22,7 @@ class GameSettings {
     return DefaultGameSettings.defaultBoardSizeValue;
   }
 
-  static int getColorsCountValueFromUnsafe(int colorsCount) {
+  static String getColorsCountValueFromUnsafe(String colorsCount) {
     if (DefaultGameSettings.allowedColorsCountValues.contains(colorsCount)) {
       return colorsCount;
     }
@@ -34,20 +38,21 @@ class GameSettings {
   }
 
   void dump() {
-    printlog('Settings: ');
-    printlog('  boardSize: $boardSize');
-    printlog('  colorsCount: $colorsCount');
+    printlog('$GameSettings:');
+    printlog('  ${DefaultGameSettings.parameterCodeBoardSize}: $boardSize');
+    printlog('  ${DefaultGameSettings.parameterCodeColorsCount}: $colorsCount');
+    printlog('');
   }
 
   @override
   String toString() {
-    return 'GameSettings(${toJson()})';
+    return '$GameSettings(${toJson()})';
   }
 
   Map<String, dynamic>? toJson() {
     return <String, dynamic>{
-      'boardSize': boardSize,
-      'colorsCount': colorsCount,
+      DefaultGameSettings.parameterCodeBoardSize: boardSize,
+      DefaultGameSettings.parameterCodeColorsCount: colorsCount,
     };
   }
 }
diff --git a/lib/models/settings_global.dart b/lib/models/settings/settings_global.dart
similarity index 58%
rename from lib/models/settings_global.dart
rename to lib/models/settings/settings_global.dart
index 231917c..8212f83 100644
--- a/lib/models/settings_global.dart
+++ b/lib/models/settings/settings_global.dart
@@ -1,16 +1,16 @@
 import 'package:jeweled/config/default_global_settings.dart';
-import 'package:jeweled/utils/tools.dart';
+import 'package:jeweled/utils/tools.dart'; 
 
 class GlobalSettings {
-  final int colorsTheme;
-  final int graphicTheme;
+  final String colorsTheme;
+  final String graphicTheme;
 
   GlobalSettings({
     required this.colorsTheme,
     required this.graphicTheme,
   });
 
-  static int getColorsThemeValueFromUnsafe(int colorsTheme) {
+  static String getColorsThemeValueFromUnsafe(String colorsTheme) {
     if (DefaultGlobalSettings.allowedColorsThemeValues.contains(colorsTheme)) {
       return colorsTheme;
     }
@@ -18,7 +18,7 @@ class GlobalSettings {
     return DefaultGlobalSettings.defaultColorsThemeValue;
   }
 
-  static int getGraphicThemeValueFromUnsafe(int graphicTheme) {
+  static String getGraphicThemeValueFromUnsafe(String graphicTheme) {
     if (DefaultGlobalSettings.allowedGraphicThemeValues.contains(graphicTheme)) {
       return graphicTheme;
     }
@@ -34,20 +34,21 @@ class GlobalSettings {
   }
 
   void dump() {
-    printlog('Settings: ');
-    printlog('  colorsTheme: $colorsTheme');
-    printlog('  graphicTheme: $graphicTheme');
+    printlog('$GlobalSettings:');
+    printlog('  ${DefaultGlobalSettings.parameterCodeColorsTheme}: $colorsTheme');
+    printlog('  ${DefaultGlobalSettings.parameterCodeGraphicsTheme}: $graphicTheme');
+    printlog('');
   }
 
   @override
   String toString() {
-    return 'GlobalSettings(${toJson()})';
+    return '$GlobalSettings(${toJson()})';
   }
 
   Map<String, dynamic>? toJson() {
     return <String, dynamic>{
-      'colorsTheme': colorsTheme,
-      'graphicTheme': graphicTheme,
+      DefaultGlobalSettings.parameterCodeColorsTheme: colorsTheme,
+      DefaultGlobalSettings.parameterCodeGraphicsTheme: graphicTheme,
     };
   }
 }
diff --git a/lib/ui/widgets/game_bottom_buttons.dart b/lib/ui/game/game_end.dart
similarity index 64%
rename from lib/ui/widgets/game_bottom_buttons.dart
rename to lib/ui/game/game_end.dart
index 4faea09..71196a0 100644
--- a/lib/ui/widgets/game_bottom_buttons.dart
+++ b/lib/ui/game/game_end.dart
@@ -3,19 +3,19 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:jeweled/cubit/game_cubit.dart';
 
-class GameBottomButtonsWidget extends StatelessWidget {
-  const GameBottomButtonsWidget({super.key});
+class GameEndWidget extends StatelessWidget {
+  const GameEndWidget({super.key});
 
   @override
   Widget build(BuildContext context) {
-    String decorationImageAssetName = 'assets/icons/game_fail.png';
+    const String decorationImageAssetName = 'assets/ui/placeholder.png';
 
-    Widget decorationWidget = TextButton(
+    const Widget decorationWidget = TextButton(
+      onPressed: null,
       child: Image(
         image: AssetImage(decorationImageAssetName),
         fit: BoxFit.fill,
       ),
-      onPressed: () {},
     );
 
     return Table(
@@ -23,24 +23,23 @@ class GameBottomButtonsWidget extends StatelessWidget {
       children: [
         TableRow(
           children: [
-            Column(
+            const Column(
               children: [decorationWidget],
             ),
             Column(
               children: [
                 TextButton(
                   child: const Image(
-                    image: AssetImage('assets/icons/button_back.png'),
+                    image: AssetImage('assets/ui/button_back.png'),
                     fit: BoxFit.fill,
                   ),
                   onPressed: () {
-                    final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
-                    gameCubit.quitGame();
+                    BlocProvider.of<GameCubit>(context).quitGame();
                   },
                 )
               ],
             ),
-            Column(
+            const Column(
               children: [decorationWidget],
             ),
           ],
diff --git a/lib/ui/widgets/game_top_indicator.dart b/lib/ui/game/game_top.dart
similarity index 90%
rename from lib/ui/widgets/game_top_indicator.dart
rename to lib/ui/game/game_top.dart
index 3d639b7..36f45da 100644
--- a/lib/ui/widgets/game_top_indicator.dart
+++ b/lib/ui/game/game_top.dart
@@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:jeweled/cubit/game_cubit.dart';
-import 'package:jeweled/models/game.dart';
+import 'package:jeweled/models/game/game.dart';
 import 'package:jeweled/ui/widgets/indicators/indicator_available_blocks.dart';
 import 'package:jeweled/ui/widgets/indicators/indicator_moves_count.dart';
 import 'package:jeweled/ui/widgets/indicators/indicator_score.dart';
 import 'package:jeweled/ui/widgets/indicators/indicator_shuffle_button.dart';
 
-class GameTopIndicatorWidget extends StatelessWidget {
-  const GameTopIndicatorWidget({super.key});
+class GameTopWidget extends StatelessWidget {
+  const GameTopWidget({super.key});
 
   @override
   Widget build(BuildContext context) {
diff --git a/lib/ui/helpers/app_titles.dart b/lib/ui/helpers/app_titles.dart
new file mode 100644
index 0000000..b98107b
--- /dev/null
+++ b/lib/ui/helpers/app_titles.dart
@@ -0,0 +1,32 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class AppHeader extends StatelessWidget {
+  const AppHeader({super.key, required this.text});
+
+  final String text;
+
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      tr(text),
+      textAlign: TextAlign.start,
+      style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2),
+    );
+  }
+}
+
+class AppTitle extends StatelessWidget {
+  const AppTitle({super.key, required this.text});
+
+  final String text;
+
+  @override
+  Widget build(BuildContext context) {
+    return Text(
+      tr(text),
+      textAlign: TextAlign.start,
+      style: Theme.of(context).textTheme.titleLarge!.apply(fontWeightDelta: 2),
+    );
+  }
+}
diff --git a/lib/ui/widgets/helpers/outlined_text_widget.dart b/lib/ui/helpers/outlined_text_widget.dart
similarity index 69%
rename from lib/ui/widgets/helpers/outlined_text_widget.dart
rename to lib/ui/helpers/outlined_text_widget.dart
index 8e33709..99c2658 100644
--- a/lib/ui/widgets/helpers/outlined_text_widget.dart
+++ b/lib/ui/helpers/outlined_text_widget.dart
@@ -1,22 +1,24 @@
 import 'package:flutter/material.dart';
 
+import 'package:jeweled/utils/color_extensions.dart';
+
 class OutlinedText extends StatelessWidget {
   const OutlinedText({
     super.key,
     required this.text,
     required this.fontSize,
     required this.textColor,
-    required this.outlineColor,
+    this.outlineColor,
   });
 
   final String text;
   final double fontSize;
   final Color textColor;
-  final Color outlineColor;
+  final Color? outlineColor;
 
   @override
   Widget build(BuildContext context) {
-    final double delta = fontSize / 35;
+    final double delta = fontSize / 30;
 
     return Text(
       text,
@@ -28,19 +30,19 @@ class OutlinedText extends StatelessWidget {
         shadows: [
           Shadow(
             offset: Offset(-delta, -delta),
-            color: outlineColor,
+            color: outlineColor ?? textColor.darken(),
           ),
           Shadow(
             offset: Offset(delta, -delta),
-            color: outlineColor,
+            color: outlineColor ?? textColor.darken(),
           ),
           Shadow(
             offset: Offset(delta, delta),
-            color: outlineColor,
+            color: outlineColor ?? textColor.darken(),
           ),
           Shadow(
             offset: Offset(-delta, delta),
-            color: outlineColor,
+            color: outlineColor ?? textColor.darken(),
           ),
         ],
       ),
diff --git a/lib/ui/widgets/game.dart b/lib/ui/layouts/game_layout.dart
similarity index 53%
rename from lib/ui/widgets/game.dart
rename to lib/ui/layouts/game_layout.dart
index 443c554..4c9e4a6 100644
--- a/lib/ui/widgets/game.dart
+++ b/lib/ui/layouts/game_layout.dart
@@ -2,13 +2,13 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:jeweled/cubit/game_cubit.dart';
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/ui/widgets/game_board.dart';
-import 'package:jeweled/ui/widgets/game_bottom_buttons.dart';
-import 'package:jeweled/ui/widgets/game_top_indicator.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/game/game_end.dart';
+import 'package:jeweled/ui/game/game_top.dart';
+import 'package:jeweled/ui/widgets/game/board.dart';
 
-class GameWidget extends StatelessWidget {
-  const GameWidget({super.key});
+class GameLayout extends StatelessWidget {
+  const GameLayout({super.key});
 
   @override
   Widget build(BuildContext context) {
@@ -17,19 +17,18 @@ class GameWidget extends StatelessWidget {
         final Game currentGame = gameState.currentGame;
 
         return Container(
+          alignment: AlignmentDirectional.topCenter,
           padding: const EdgeInsets.all(4),
           child: Column(
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
+              const GameTopWidget(),
               const SizedBox(height: 8),
-              const GameTopIndicatorWidget(),
-              const SizedBox(height: 2),
-              const GameBoard(),
-              const SizedBox(height: 2),
-              currentGame.isFinished
-                  ? const GameBottomButtonsWidget()
-                  : const SizedBox.shrink(),
+              const BoardWidget(),
+              const SizedBox(height: 8),
+              const Expanded(child: SizedBox.shrink()),
+              currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(),
             ],
           ),
         );
diff --git a/lib/ui/widgets/parameters.dart b/lib/ui/layouts/parameters_layout.dart
similarity index 51%
rename from lib/ui/widgets/parameters.dart
rename to lib/ui/layouts/parameters_layout.dart
index c8f858a..6432f85 100644
--- a/lib/ui/widgets/parameters.dart
+++ b/lib/ui/layouts/parameters_layout.dart
@@ -3,76 +3,20 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:jeweled/config/default_game_settings.dart';
 import 'package:jeweled/config/default_global_settings.dart';
-import 'package:jeweled/cubit/game_cubit.dart';
 import 'package:jeweled/cubit/settings_game_cubit.dart';
 import 'package:jeweled/cubit/settings_global_cubit.dart';
-import 'package:jeweled/ui/painters/parameter_painter.dart';
+import 'package:jeweled/ui/parameters/parameter_image.dart';
+import 'package:jeweled/ui/parameters/parameter_painter.dart';
+import 'package:jeweled/ui/widgets/actions/button_delete_saved_game.dart';
+import 'package:jeweled/ui/widgets/actions/button_game_start_new.dart';
+import 'package:jeweled/ui/widgets/actions/button_resume_saved_game.dart';
 
-class Parameters extends StatelessWidget {
-  const Parameters({super.key});
+class ParametersLayout extends StatelessWidget {
+  const ParametersLayout({super.key, required this.canResume});
 
-  final double separatorHeight = 8.0;
-
-  List<Widget> buildParametersLine({
-    required String code,
-    required bool isGlobal,
-  }) {
-    final List<Widget> parameterButtons = [];
-
-    final List<int> availableValues = isGlobal
-        ? DefaultGlobalSettings.getAvailableValues(code)
-        : DefaultGameSettings.getAvailableValues(code);
-
-    for (int value in availableValues) {
-      final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>(
-        builder: (BuildContext context, GameSettingsState gameSettingsState) {
-          return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
-            builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
-              final GameSettingsCubit gameSettingsCubit =
-                  BlocProvider.of<GameSettingsCubit>(context);
-              final GlobalSettingsCubit globalSettingsCubit =
-                  BlocProvider.of<GlobalSettingsCubit>(context);
-
-              final int currentValue = isGlobal
-                  ? globalSettingsCubit.getParameterValue(code)
-                  : gameSettingsCubit.getParameterValue(code);
-
-              final bool isActive = (value == currentValue);
+  final bool canResume;
 
-              final double displayWidth = MediaQuery.of(context).size.width;
-              final double itemWidth = displayWidth / availableValues.length - 25;
-
-              return TextButton(
-                child: Container(
-                  margin: const EdgeInsets.all(0),
-                  padding: const EdgeInsets.all(0),
-                  child: CustomPaint(
-                    size: Size(itemWidth, itemWidth),
-                    willChange: false,
-                    painter: ParameterPainter(
-                      code: code,
-                      value: value,
-                      isSelected: isActive,
-                      gameSettings: gameSettingsState.settings,
-                      globalSettings: globalSettingsState.settings,
-                    ),
-                    isComplex: true,
-                  ),
-                ),
-                onPressed: () => isGlobal
-                    ? globalSettingsCubit.setParameterValue(code, value)
-                    : gameSettingsCubit.setParameterValue(code, value),
-              );
-            },
-          );
-        },
-      );
-
-      parameterButtons.add(parameterButton);
-    }
-
-    return parameterButtons;
-  }
+  final double separatorHeight = 8.0;
 
   @override
   Widget build(BuildContext context) {
@@ -92,7 +36,24 @@ class Parameters extends StatelessWidget {
     }
 
     lines.add(SizedBox(height: separatorHeight));
-    lines.add(Expanded(child: buildStartNewGameButton()));
+
+    if (canResume == false) {
+      // Start new game
+      lines.add(const Expanded(
+        child: StartNewGameButton(),
+      ));
+    } else {
+      // Resume game
+      lines.add(const Expanded(
+        child: ResumeSavedGameButton(),
+      ));
+      // Delete saved game
+      lines.add(SizedBox.square(
+        dimension: MediaQuery.of(context).size.width / 4,
+        child: const DeleteSavedGameButton(),
+      ));
+    }
+
     lines.add(SizedBox(height: separatorHeight));
 
     // Global settings
@@ -113,47 +74,81 @@ class Parameters extends StatelessWidget {
     );
   }
 
-  static Image buildImageWidget(String imageAssetCode) {
-    return Image(
-      image: AssetImage('assets/icons/$imageAssetCode.png'),
-      fit: BoxFit.fill,
-    );
-  }
+  List<Widget> buildParametersLine({
+    required String code,
+    required bool isGlobal,
+  }) {
+    final List<Widget> parameterButtons = [];
 
-  static Container buildImageContainerWidget(String imageAssetCode) {
-    return Container(
-      child: buildImageWidget(imageAssetCode),
-    );
-  }
+    final List<String> availableValues = isGlobal
+        ? DefaultGlobalSettings.getAvailableValues(code)
+        : DefaultGameSettings.getAvailableValues(code);
 
-  static Column buildDecorationImageWidget() {
-    return Column(
-      children: [
-        TextButton(
-          child: buildImageContainerWidget('placeholder'),
-          onPressed: () {},
-        ),
-      ],
-    );
-  }
+    if (availableValues.length <= 1) {
+      return [];
+    }
 
-  static Widget buildStartNewGameButton() {
-    return BlocBuilder<GameSettingsCubit, GameSettingsState>(
-      builder: (BuildContext context, GameSettingsState gameSettingsState) {
-        return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
-          builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
-            final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
-
-            return TextButton(
-              child: buildImageContainerWidget('button_start'),
-              onPressed: () => gameCubit.startNewGame(
-                gameSettings: gameSettingsState.settings,
-                globalSettings: globalSettingsState.settings,
-              ),
-            );
-          },
-        );
-      },
-    );
+    for (String value in availableValues) {
+      final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>(
+        builder: (BuildContext context, GameSettingsState gameSettingsState) {
+          return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
+            builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
+              final GameSettingsCubit gameSettingsCubit =
+                  BlocProvider.of<GameSettingsCubit>(context);
+              final GlobalSettingsCubit globalSettingsCubit =
+                  BlocProvider.of<GlobalSettingsCubit>(context);
+
+              final String currentValue = isGlobal
+                  ? globalSettingsCubit.getParameterValue(code)
+                  : gameSettingsCubit.getParameterValue(code);
+
+              final bool isActive = (value == currentValue);
+
+              final double displayWidth = MediaQuery.of(context).size.width;
+              final double itemWidth = displayWidth / availableValues.length - 26;
+
+              final bool displayedWithAssets =
+                  DefaultGlobalSettings.displayedWithAssets.contains(code) ||
+                      DefaultGameSettings.displayedWithAssets.contains(code);
+
+              return TextButton(
+                child: Container(
+                  child: displayedWithAssets
+                      ? SizedBox.square(
+                          dimension: itemWidth,
+                          child: ParameterImage(
+                            code: code,
+                            value: value,
+                            isSelected: isActive,
+                          ),
+                        )
+                      : CustomPaint(
+                          size: Size(itemWidth, itemWidth),
+                          willChange: false,
+                          painter: ParameterPainter(
+                            code: code,
+                            value: value,
+                            isSelected: isActive,
+                            gameSettings: gameSettingsState.settings,
+                            globalSettings: globalSettingsState.settings,
+                          ),
+                          isComplex: true,
+                        ),
+                ),
+                onPressed: () {
+                  isGlobal
+                      ? globalSettingsCubit.setParameterValue(code, value)
+                      : gameSettingsCubit.setParameterValue(code, value);
+                },
+              );
+            },
+          );
+        },
+      );
+
+      parameterButtons.add(parameterButton);
+    }
+
+    return parameterButtons;
   }
 }
diff --git a/lib/ui/painters/game_board_painter.dart b/lib/ui/painters/game_board_painter.dart
index 2b99234..2c490e4 100644
--- a/lib/ui/painters/game_board_painter.dart
+++ b/lib/ui/painters/game_board_painter.dart
@@ -3,11 +3,11 @@ import 'dart:ui' as ui;
 
 import 'package:flutter/material.dart';
 
+import 'package:jeweled/config/color_theme.dart';
 import 'package:jeweled/config/default_global_settings.dart';
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/models/cell_location.dart';
+import 'package:jeweled/models/game/cell_location.dart';
+import 'package:jeweled/models/game/game.dart';
 import 'package:jeweled/utils/color_extensions.dart';
-import 'package:jeweled/utils/color_theme.dart';
 
 class GameBoardPainter extends CustomPainter {
   const GameBoardPainter({
@@ -20,7 +20,7 @@ class GameBoardPainter extends CustomPainter {
 
   @override
   void paint(Canvas canvas, Size size) {
-    int graphicTheme = game.globalSettings.graphicTheme;
+    String graphicTheme = game.globalSettings.graphicTheme;
 
     final double canvasSize = min(size.width, size.height);
 
@@ -97,9 +97,9 @@ class GameBoardPainter extends CustomPainter {
     double borderWidth = 0,
     List<String>? contentStrings,
   }) {
-    final int cellsCountHorizontal = game.gameSettings.boardSize;
-    final int cellsCountVertical = game.gameSettings.boardSize;
-    final int colorsTheme = game.globalSettings.colorsTheme;
+    final int cellsCountHorizontal = game.gameSettings.boardSizeValue;
+    final int cellsCountVertical = game.gameSettings.boardSizeValue;
+    final String colorsTheme = game.globalSettings.colorsTheme;
 
     final double size = canvasSize / max(cellsCountHorizontal, cellsCountVertical);
 
@@ -138,7 +138,7 @@ class GameBoardPainter extends CustomPainter {
     required double y,
     required double cellSize,
     required int cellValue,
-    required int colorsTheme,
+    required String colorsTheme,
     required double overlapping,
     required int gradientFrom,
     required int gradientTo,
diff --git a/lib/ui/parameters/parameter_image.dart b/lib/ui/parameters/parameter_image.dart
new file mode 100644
index 0000000..fc4b576
--- /dev/null
+++ b/lib/ui/parameters/parameter_image.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+class ParameterImage extends StatelessWidget {
+  const ParameterImage({
+    super.key,
+    required this.code,
+    required this.value,
+    required this.isSelected,
+  });
+
+  final String code;
+  final String value;
+  final bool isSelected;
+
+  static const Color buttonBackgroundColor = Colors.white;
+  static const Color buttonBorderColorActive = Colors.blue;
+  static const Color buttonBorderColorInactive = Colors.white;
+  static const double buttonBorderWidth = 8.0;
+  static const double buttonBorderRadius = 8.0;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      decoration: BoxDecoration(
+        color: buttonBackgroundColor,
+        borderRadius: BorderRadius.circular(buttonBorderRadius),
+        border: Border.all(
+          color: isSelected ? buttonBorderColorActive : buttonBorderColorInactive,
+          width: buttonBorderWidth,
+        ),
+      ),
+      child: Image(
+        image: AssetImage('assets/ui/${code}_$value.png'),
+        fit: BoxFit.fill,
+      ),
+    );
+  }
+}
diff --git a/lib/ui/painters/parameter_painter.dart b/lib/ui/parameters/parameter_painter.dart
similarity index 92%
rename from lib/ui/painters/parameter_painter.dart
rename to lib/ui/parameters/parameter_painter.dart
index 5e4a952..7399851 100644
--- a/lib/ui/painters/parameter_painter.dart
+++ b/lib/ui/parameters/parameter_painter.dart
@@ -3,12 +3,12 @@ import 'dart:ui' as ui;
 
 import 'package:flutter/material.dart';
 
+import 'package:jeweled/config/color_theme.dart';
 import 'package:jeweled/config/default_game_settings.dart';
 import 'package:jeweled/config/default_global_settings.dart';
-import 'package:jeweled/models/settings_game.dart';
-import 'package:jeweled/models/settings_global.dart';
+import 'package:jeweled/models/settings/settings_game.dart';
+import 'package:jeweled/models/settings/settings_global.dart';
 import 'package:jeweled/utils/color_extensions.dart';
-import 'package:jeweled/utils/color_theme.dart';
 import 'package:jeweled/utils/tools.dart';
 
 class ParameterPainter extends CustomPainter {
@@ -21,7 +21,7 @@ class ParameterPainter extends CustomPainter {
   });
 
   final String code;
-  final int value;
+  final String value;
   final bool isSelected;
   final GameSettings gameSettings;
   final GlobalSettings globalSettings;
@@ -39,22 +39,22 @@ class ParameterPainter extends CustomPainter {
     paint.style = PaintingStyle.stroke;
     paint.color = isSelected ? borderColorEnabled : borderColorDisabled;
     paint.strokeJoin = StrokeJoin.round;
-    paint.strokeWidth = 20 / 100 * canvasSize;
+    paint.strokeWidth = 10;
     canvas.drawRect(
         Rect.fromPoints(const Offset(0, 0), Offset(canvasSize, canvasSize)), paint);
 
     // content
     switch (code) {
-      case 'colorsCount':
+      case DefaultGameSettings.parameterCodeColorsCount:
         paintColorsCountParameterItem(value, canvas, canvasSize);
         break;
-      case 'boardSize':
+      case DefaultGameSettings.parameterCodeBoardSize:
         paintBoardSizeParameterItem(value, canvas, canvasSize);
         break;
-      case 'colorsTheme':
+      case DefaultGlobalSettings.parameterCodeColorsTheme:
         paintColorsThemeParameterItem(value, canvas, canvasSize);
         break;
-      case 'graphicTheme':
+      case DefaultGlobalSettings.parameterCodeGraphicsTheme:
         paintGraphicThemeParameterItem(value, canvas, canvasSize);
         break;
       default:
@@ -68,15 +68,15 @@ class ParameterPainter extends CustomPainter {
     return false;
   }
 
-  // "unknown" parameter -> simple bock with text
+  // "unknown" parameter -> simple block with text
   void paintUnknownParameterItem(
-    final int value,
+    final String value,
     final Canvas canvas,
     final double size,
   ) {
     final paint = Paint();
     paint.strokeJoin = StrokeJoin.round;
-    paint.strokeWidth = 3 / 100 * size;
+    paint.strokeWidth = 3;
 
     paint.color = Colors.grey;
     paint.style = PaintingStyle.fill;
@@ -93,6 +93,7 @@ class ParameterPainter extends CustomPainter {
     final textPainter = TextPainter(
       text: textSpan,
       textDirection: TextDirection.ltr,
+      textAlign: TextAlign.center,
     );
     textPainter.layout();
     textPainter.paint(
@@ -105,7 +106,7 @@ class ParameterPainter extends CustomPainter {
   }
 
   void paintBoardSizeParameterItem(
-    final int value,
+    final String value,
     final Canvas canvas,
     final double size,
   ) {
@@ -168,7 +169,7 @@ class ParameterPainter extends CustomPainter {
   }
 
   void paintColorsCountParameterItem(
-    final int value,
+    final String value,
     final Canvas canvas,
     final double size,
   ) {
@@ -193,7 +194,7 @@ class ParameterPainter extends CustomPainter {
 
     final paint = Paint();
     paint.strokeJoin = StrokeJoin.round;
-    paint.strokeWidth = 3 / 100 * size;
+    paint.strokeWidth = 3;
 
     // Colored background
     paint.color = backgroundColor;
@@ -216,7 +217,9 @@ class ParameterPainter extends CustomPainter {
     final double margin = 3 / 100 * size;
     final double width = ((size - 2 * padding) / 3) - 2 * margin;
 
-    for (int colorIndex = 0; colorIndex < value; colorIndex++) {
+    final colorsCount = int.parse(value);
+
+    for (int colorIndex = 0; colorIndex < colorsCount; colorIndex++) {
       final Offset position = positions[colorIndex];
 
       final Offset topLeft = Offset(padding + margin + position.dx * (width + 2 * margin),
@@ -248,6 +251,7 @@ class ParameterPainter extends CustomPainter {
     final textPainter = TextPainter(
       text: textSpan,
       textDirection: TextDirection.ltr,
+      textAlign: TextAlign.center,
     );
     textPainter.layout();
     textPainter.paint(
@@ -260,7 +264,7 @@ class ParameterPainter extends CustomPainter {
   }
 
   void paintColorsThemeParameterItem(
-    final int value,
+    final String value,
     final Canvas canvas,
     final double size,
   ) {
@@ -301,7 +305,7 @@ class ParameterPainter extends CustomPainter {
   }
 
   void paintGraphicThemeParameterItem(
-    final int value,
+    final String value,
     final Canvas canvas,
     final double size,
   ) {
@@ -309,7 +313,7 @@ class ParameterPainter extends CustomPainter {
 
     final paint = Paint();
     paint.strokeJoin = StrokeJoin.round;
-    paint.strokeWidth = 3 / 100 * size;
+    paint.strokeWidth = 3;
 
     // Colored background
     paint.color = backgroundColor;
@@ -399,6 +403,7 @@ class ParameterPainter extends CustomPainter {
         final textPainter = TextPainter(
           text: textSpan,
           textDirection: TextDirection.ltr,
+          textAlign: TextAlign.center,
         );
         textPainter.layout();
         textPainter.paint(
diff --git a/lib/ui/screens/game_page.dart b/lib/ui/screens/game_page.dart
deleted file mode 100644
index 24694bc..0000000
--- a/lib/ui/screens/game_page.dart
+++ /dev/null
@@ -1,22 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import 'package:jeweled/cubit/game_cubit.dart';
-import 'package:jeweled/ui/widgets/game.dart';
-import 'package:jeweled/ui/widgets/parameters.dart';
-
-class GamePage extends StatelessWidget {
-  const GamePage({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: BlocBuilder<GameCubit, GameState>(
-        builder: (BuildContext context, GameState gameState) {
-          return gameState.currentGame.isRunning ? const GameWidget() : const Parameters();
-        },
-      ),
-    );
-  }
-}
diff --git a/lib/ui/screens/about_page.dart b/lib/ui/screens/page_about.dart
similarity index 86%
rename from lib/ui/screens/about_page.dart
rename to lib/ui/screens/page_about.dart
index 0ffecbc..f359df1 100644
--- a/lib/ui/screens/about_page.dart
+++ b/lib/ui/screens/page_about.dart
@@ -2,10 +2,10 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:package_info_plus/package_info_plus.dart';
 
-import 'package:jeweled/ui/widgets/header_app.dart';
+import 'package:jeweled/ui/helpers/app_titles.dart';
 
-class AboutPage extends StatelessWidget {
-  const AboutPage({super.key});
+class PageAbout extends StatelessWidget {
+  const PageAbout({super.key});
 
   @override
   Widget build(BuildContext context) {
@@ -17,7 +17,7 @@ class AboutPage extends StatelessWidget {
         mainAxisSize: MainAxisSize.max,
         children: <Widget>[
           const SizedBox(height: 8),
-          const AppHeader(text: 'about_title'),
+          const AppTitle(text: 'about_title'),
           const Text('about_content').tr(),
           FutureBuilder<PackageInfo>(
             future: PackageInfo.fromPlatform(),
diff --git a/lib/ui/screens/page_game.dart b/lib/ui/screens/page_game.dart
new file mode 100644
index 0000000..a7e3807
--- /dev/null
+++ b/lib/ui/screens/page_game.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/layouts/game_layout.dart';
+import 'package:jeweled/ui/layouts/parameters_layout.dart';
+
+class PageGame extends StatelessWidget {
+  const PageGame({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameCubit, GameState>(
+      builder: (BuildContext context, GameState gameState) {
+        final Game currentGame = gameState.currentGame;
+
+        return currentGame.isRunning
+            ? const GameLayout()
+            : ParametersLayout(canResume: currentGame.canBeResumed);
+      },
+    );
+  }
+}
diff --git a/lib/ui/screens/settings_page.dart b/lib/ui/screens/page_settings.dart
similarity index 66%
rename from lib/ui/screens/settings_page.dart
rename to lib/ui/screens/page_settings.dart
index 0bf6979..51389bc 100644
--- a/lib/ui/screens/settings_page.dart
+++ b/lib/ui/screens/page_settings.dart
@@ -1,10 +1,10 @@
 import 'package:flutter/material.dart';
 
-import 'package:jeweled/ui/widgets/header_app.dart';
-import 'package:jeweled/ui/widgets/settings/settings_form.dart';
+import 'package:jeweled/ui/helpers/app_titles.dart';
+import 'package:jeweled/ui/settings/settings_form.dart';
 
-class SettingsPage extends StatelessWidget {
-  const SettingsPage({super.key});
+class PageSettings extends StatelessWidget {
+  const PageSettings({super.key});
 
   @override
   Widget build(BuildContext context) {
@@ -16,7 +16,7 @@ class SettingsPage extends StatelessWidget {
         mainAxisSize: MainAxisSize.max,
         children: <Widget>[
           SizedBox(height: 8),
-          AppHeader(text: 'settings_title'),
+          AppTitle(text: 'settings_title'),
           SizedBox(height: 8),
           SettingsForm(),
         ],
diff --git a/lib/ui/widgets/settings/settings_form.dart b/lib/ui/settings/settings_form.dart
similarity index 96%
rename from lib/ui/widgets/settings/settings_form.dart
rename to lib/ui/settings/settings_form.dart
index 9ae13f3..703e9c9 100644
--- a/lib/ui/widgets/settings/settings_form.dart
+++ b/lib/ui/settings/settings_form.dart
@@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:unicons/unicons.dart';
 
-import 'package:jeweled/ui/widgets/settings/theme_card.dart';
+import 'package:jeweled/ui/settings/theme_card.dart';
 
 class SettingsForm extends StatefulWidget {
   const SettingsForm({super.key});
diff --git a/lib/ui/widgets/settings/theme_card.dart b/lib/ui/settings/theme_card.dart
similarity index 100%
rename from lib/ui/widgets/settings/theme_card.dart
rename to lib/ui/settings/theme_card.dart
diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart
index 38225b2..8513856 100644
--- a/lib/ui/skeleton.dart
+++ b/lib/ui/skeleton.dart
@@ -18,7 +18,7 @@ class SkeletonScreen extends StatelessWidget {
           return Menu.getPageWidget(pageIndex);
         },
       ),
-      backgroundColor: Theme.of(context).colorScheme.background,
+      backgroundColor: Theme.of(context).colorScheme.surface,
     );
   }
 }
diff --git a/lib/ui/widgets/actions/button_delete_saved_game.dart b/lib/ui/widgets/actions/button_delete_saved_game.dart
new file mode 100644
index 0000000..27b5828
--- /dev/null
+++ b/lib/ui/widgets/actions/button_delete_saved_game.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+
+class DeleteSavedGameButton extends StatelessWidget {
+  const DeleteSavedGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/ui/button_delete_saved_game.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () {
+        BlocProvider.of<GameCubit>(context).deleteSavedGame();
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/actions/button_game_quit.dart b/lib/ui/widgets/actions/button_game_quit.dart
new file mode 100644
index 0000000..b6f561c
--- /dev/null
+++ b/lib/ui/widgets/actions/button_game_quit.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+
+class QuitGameButton extends StatelessWidget {
+  const QuitGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/ui/button_back.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () {
+        BlocProvider.of<GameCubit>(context).quitGame();
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/actions/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart
new file mode 100644
index 0000000..d946a01
--- /dev/null
+++ b/lib/ui/widgets/actions/button_game_start_new.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+import 'package:jeweled/cubit/settings_game_cubit.dart';
+import 'package:jeweled/cubit/settings_global_cubit.dart';
+
+class StartNewGameButton extends StatelessWidget {
+  const StartNewGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<GameSettingsCubit, GameSettingsState>(
+      builder: (BuildContext context, GameSettingsState gameSettingsState) {
+        return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
+          builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
+            return TextButton(
+              child: const Image(
+                image: AssetImage('assets/ui/button_start.png'),
+                fit: BoxFit.fill,
+              ),
+              onPressed: () {
+                BlocProvider.of<GameCubit>(context).startNewGame(
+                  gameSettings: gameSettingsState.settings,
+                  globalSettings: globalSettingsState.settings,
+                );
+              },
+            );
+          },
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/actions/button_resume_saved_game.dart b/lib/ui/widgets/actions/button_resume_saved_game.dart
new file mode 100644
index 0000000..c75cce7
--- /dev/null
+++ b/lib/ui/widgets/actions/button_resume_saved_game.dart
@@ -0,0 +1,21 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:jeweled/cubit/game_cubit.dart';
+
+class ResumeSavedGameButton extends StatelessWidget {
+  const ResumeSavedGameButton({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return TextButton(
+      child: const Image(
+        image: AssetImage('assets/ui/button_resume_game.png'),
+        fit: BoxFit.fill,
+      ),
+      onPressed: () {
+        BlocProvider.of<GameCubit>(context).resumeSavedGame();
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/game_board.dart b/lib/ui/widgets/game/board.dart
similarity index 81%
rename from lib/ui/widgets/game_board.dart
rename to lib/ui/widgets/game/board.dart
index beba562..5daec54 100644
--- a/lib/ui/widgets/game_board.dart
+++ b/lib/ui/widgets/game/board.dart
@@ -2,18 +2,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:jeweled/cubit/game_cubit.dart';
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/models/cell_location.dart';
+import 'package:jeweled/models/game/cell_location.dart';
+import 'package:jeweled/models/game/game.dart';
 import 'package:jeweled/ui/painters/game_board_painter.dart';
 
-class GameBoard extends StatefulWidget {
-  const GameBoard({super.key});
+class BoardWidget extends StatefulWidget {
+  const BoardWidget({super.key});
 
   @override
-  State<GameBoard> createState() => _GameBoard();
+  State<BoardWidget> createState() => _BoardWidget();
 }
 
-class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
+class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin {
   final int animationDuration = 500;
 
   List<List<Animation<double>?>> animations = [];
@@ -57,7 +57,7 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
             if (status == AnimationStatus.completed) {
               gameCubit.postAnimate();
 
-              resetAnimations(currentGame.gameSettings.boardSize);
+              resetAnimations(currentGame.gameSettings.boardSizeValue);
               setState(() {});
 
               controller.dispose();
@@ -66,9 +66,9 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
 
     // Count translation length for each cell to move
     final List<List<int>> stepsDownCounts = List.generate(
-      currentGame.gameSettings.boardSize,
+      currentGame.gameSettings.boardSizeValue,
       (i) => List.generate(
-        currentGame.gameSettings.boardSize,
+        currentGame.gameSettings.boardSizeValue,
         (i) => 0,
       ),
     );
@@ -105,7 +105,7 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
           final Game currentGame = gameState.currentGame;
 
           if (animations.isEmpty) {
-            resetAnimations(currentGame.gameSettings.boardSize);
+            resetAnimations(currentGame.gameSettings.boardSizeValue);
           }
 
           return GestureDetector(
@@ -122,11 +122,13 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
               if (!animationInProgress) {
                 final double xTap = details.localPosition.dx;
                 final double yTap = details.localPosition.dy;
-                final int col = xTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
-                final int row = yTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
+                final int col =
+                    xTap ~/ (displayWidth / currentGame.gameSettings.boardSizeValue);
+                final int row =
+                    yTap ~/ (displayWidth / currentGame.gameSettings.boardSizeValue);
 
-                final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
-                animateCells(gameCubit.tapOnCell(CellLocation.go(row, col)));
+                animateCells(
+                    BlocProvider.of<GameCubit>(context).tapOnCell(CellLocation.go(row, col)));
               }
             },
             child: CustomPaint(
diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart
index 51a5fe0..d09ae4d 100644
--- a/lib/ui/widgets/global_app_bar.dart
+++ b/lib/ui/widgets/global_app_bar.dart
@@ -4,8 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:jeweled/config/menu.dart';
 import 'package:jeweled/cubit/game_cubit.dart';
 import 'package:jeweled/cubit/nav_cubit.dart';
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/ui/widgets/helpers/app_titles.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/helpers/app_titles.dart';
 
 class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
   const GlobalAppBar({super.key});
@@ -20,16 +20,15 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
 
             final List<Widget> menuActions = [];
 
-            if (currentGame.isRunning) {
+            if (currentGame.isRunning && !currentGame.isFinished) {
               menuActions.add(TextButton(
                 child: const Image(
-                  image: AssetImage('assets/icons/button_back.png'),
+                  image: AssetImage('assets/ui/button_back.png'),
                   fit: BoxFit.fill,
                 ),
                 onPressed: () {},
                 onLongPress: () {
-                  final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
-                  gameCubit.quitGame();
+                  BlocProvider.of<GameCubit>(context).quitGame();
                 },
               ));
             } else {
@@ -37,7 +36,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
                 // go to Settings page
                 menuActions.add(ElevatedButton(
                   onPressed: () {
-                    context.read<NavCubit>().switchToSettingsPage();
+                    context.read<NavCubit>().goToSettingsPage();
                   },
                   style: ElevatedButton.styleFrom(
                     shape: const CircleBorder(),
@@ -48,7 +47,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
                 // go to About page
                 menuActions.add(ElevatedButton(
                   onPressed: () {
-                    context.read<NavCubit>().switchToAboutPage();
+                    context.read<NavCubit>().goToAboutPage();
                   },
                   style: ElevatedButton.styleFrom(
                     shape: const CircleBorder(),
diff --git a/lib/ui/widgets/header_app.dart b/lib/ui/widgets/header_app.dart
deleted file mode 100644
index b5c5be0..0000000
--- a/lib/ui/widgets/header_app.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-class AppHeader extends StatelessWidget {
-  const AppHeader({super.key, required this.text});
-
-  final String text;
-
-  @override
-  Widget build(BuildContext context) {
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.start,
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        Text(
-          tr(text),
-          textAlign: TextAlign.start,
-          style: Theme.of(context).textTheme.headlineSmall!.apply(fontWeightDelta: 2),
-        ),
-        const SizedBox(height: 8),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/helpers/app_titles.dart b/lib/ui/widgets/helpers/app_titles.dart
deleted file mode 100644
index 7cbbb20..0000000
--- a/lib/ui/widgets/helpers/app_titles.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-class AppTitle extends StatelessWidget {
-  const AppTitle({super.key, required this.text});
-
-  final String text;
-
-  @override
-  Widget build(BuildContext context) {
-    return Text(
-      tr(text),
-      textAlign: TextAlign.start,
-      style: Theme.of(context).textTheme.headlineLarge!.apply(fontWeightDelta: 2),
-    );
-  }
-}
diff --git a/lib/ui/widgets/indicators/indicator_available_blocks.dart b/lib/ui/widgets/indicators/indicator_available_blocks.dart
index 865b933..acffb94 100644
--- a/lib/ui/widgets/indicators/indicator_available_blocks.dart
+++ b/lib/ui/widgets/indicators/indicator_available_blocks.dart
@@ -2,8 +2,8 @@ import 'dart:math';
 
 import 'package:flutter/material.dart';
 
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/helpers/outlined_text_widget.dart';
 import 'package:jeweled/utils/color_extensions.dart';
 
 class AvailableBlocksCountIndicator extends StatelessWidget {
diff --git a/lib/ui/widgets/indicators/indicator_moves_count.dart b/lib/ui/widgets/indicators/indicator_moves_count.dart
index f6ebaa8..8693ab3 100644
--- a/lib/ui/widgets/indicators/indicator_moves_count.dart
+++ b/lib/ui/widgets/indicators/indicator_moves_count.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/helpers/outlined_text_widget.dart';
 import 'package:jeweled/utils/color_extensions.dart';
 
 class MovesCountsIndicator extends StatelessWidget {
diff --git a/lib/ui/widgets/indicators/indicator_score.dart b/lib/ui/widgets/indicators/indicator_score.dart
index 6f7dc83..97f9402 100644
--- a/lib/ui/widgets/indicators/indicator_score.dart
+++ b/lib/ui/widgets/indicators/indicator_score.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/helpers/outlined_text_widget.dart';
 import 'package:jeweled/utils/color_extensions.dart';
 
 class ScoreIndicator extends StatelessWidget {
diff --git a/lib/ui/widgets/indicators/indicator_shuffle_button.dart b/lib/ui/widgets/indicators/indicator_shuffle_button.dart
index 5688bf1..26cc17f 100644
--- a/lib/ui/widgets/indicators/indicator_shuffle_button.dart
+++ b/lib/ui/widgets/indicators/indicator_shuffle_button.dart
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:jeweled/cubit/game_cubit.dart';
-import 'package:jeweled/models/game.dart';
-import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart';
+import 'package:jeweled/models/game/game.dart';
+import 'package:jeweled/ui/helpers/outlined_text_widget.dart';
 import 'package:jeweled/utils/color_extensions.dart';
 
 class ShuffleButton extends StatelessWidget {
@@ -24,8 +24,7 @@ class ShuffleButton extends StatelessWidget {
         outlineColor: outlineColor,
       ),
       onPressed: () {
-        final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
-        gameCubit.shuffleColors(game.globalSettings.colorsTheme);
+        BlocProvider.of<GameCubit>(context).shuffleColors(game.globalSettings.colorsTheme);
       },
     );
   }
diff --git a/pubspec.lock b/pubspec.lock
index 25b5be0..e0ab96e 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -61,10 +61,10 @@ packages:
     dependency: "direct main"
     description:
       name: easy_localization
-      sha256: "432698c31a488dd64c56d4759f20d04844baba5e9e4f2cb1abb9676257918b17"
+      sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.6"
+    version: "3.0.7"
   easy_logger:
     dependency: transitive
     description:
@@ -106,18 +106,18 @@ packages:
     dependency: "direct main"
     description:
       name: flutter_bloc
-      sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2
+      sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
       url: "https://pub.dev"
     source: hosted
-    version: "8.1.5"
+    version: "8.1.6"
   flutter_lints:
     dependency: "direct dev"
     description:
       name: flutter_lints
-      sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
+      sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.2"
+    version: "4.0.0"
   flutter_localizations:
     dependency: transitive
     description: flutter
@@ -164,18 +164,18 @@ packages:
     dependency: transitive
     description:
       name: intl
-      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
       url: "https://pub.dev"
     source: hosted
-    version: "0.18.1"
+    version: "0.19.0"
   lints:
     dependency: transitive
     description:
       name: lints
-      sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
+      sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.0"
+    version: "4.0.0"
   material_color_utilities:
     dependency: transitive
     description:
@@ -188,10 +188,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
+      sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
       url: "https://pub.dev"
     source: hosted
-    version: "1.11.0"
+    version: "1.12.0"
   nested:
     dependency: transitive
     description:
@@ -236,18 +236,18 @@ packages:
     dependency: transitive
     description:
       name: path_provider_android
-      sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
+      sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.4"
+    version: "2.2.5"
   path_provider_foundation:
     dependency: transitive
     description:
       name: path_provider_foundation
-      sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
+      sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.2"
+    version: "2.4.0"
   path_provider_linux:
     dependency: transitive
     description:
@@ -276,10 +276,10 @@ packages:
     dependency: transitive
     description:
       name: platform
-      sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
+      sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.4"
+    version: "3.1.5"
   plugin_platform_interface:
     dependency: transitive
     description:
@@ -308,18 +308,18 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
+      sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.2"
+    version: "2.2.3"
   shared_preferences_foundation:
     dependency: transitive
     description:
       name: shared_preferences_foundation
-      sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
+      sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.5"
+    version: "2.4.0"
   shared_preferences_linux:
     dependency: transitive
     description:
@@ -425,10 +425,10 @@ packages:
     dependency: transitive
     description:
       name: win32
-      sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
+      sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
       url: "https://pub.dev"
     source: hosted
-    version: "5.5.0"
+    version: "5.5.1"
   xdg_directories:
     dependency: transitive
     description:
@@ -438,5 +438,5 @@ packages:
     source: hosted
     version: "1.0.4"
 sdks:
-  dart: ">=3.3.0 <4.0.0"
-  flutter: ">=3.19.0"
+  dart: ">=3.4.0 <4.0.0"
+  flutter: ">=3.22.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 03827d3..470cf2c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Jeweled Game
 
 publish_to: "none"
 
-version: 0.0.31+31
+version: 0.1.0+32
 
 environment:
   sdk: "^3.0.0"
@@ -12,6 +12,7 @@ dependencies:
   flutter:
     sdk: flutter
 
+  # base
   easy_localization: ^3.0.1
   equatable: ^2.0.5
   flutter_bloc: ^8.1.1
@@ -21,13 +22,16 @@ dependencies:
   path_provider: ^2.0.11
   unicons: ^2.1.1
 
+  # specific
+  # (none)
+
 dev_dependencies:
-  flutter_lints: ^3.0.1
+  flutter_lints: ^4.0.0
 
 flutter:
   uses-material-design: true
   assets:
-    - assets/icons/
+    - assets/ui/
     - assets/translations/
 
   fonts:
@@ -41,3 +45,4 @@ flutter:
           weight: 400
         - asset: assets/fonts/Nunito-Light.ttf
           weight: 300
+
diff --git a/icons/build_application_icons.sh b/resources/app/build_application_resources.sh
similarity index 98%
rename from icons/build_application_icons.sh
rename to resources/app/build_application_resources.sh
index 27dbe26..6d67b8f 100755
--- a/icons/build_application_icons.sh
+++ b/resources/app/build_application_resources.sh
@@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins
 command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
 
 CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
-BASE_DIR="$(dirname "${CURRENT_DIR}")"
+BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
 
 SOURCE_ICON="${CURRENT_DIR}/icon.svg"
 SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg"
diff --git a/icons/featureGraphic.svg b/resources/app/featureGraphic.svg
similarity index 100%
rename from icons/featureGraphic.svg
rename to resources/app/featureGraphic.svg
diff --git a/icons/icon.svg b/resources/app/icon.svg
similarity index 100%
rename from icons/icon.svg
rename to resources/app/icon.svg
diff --git a/resources/build_resources.sh b/resources/build_resources.sh
new file mode 100755
index 0000000..659697a
--- /dev/null
+++ b/resources/build_resources.sh
@@ -0,0 +1,7 @@
+#! /bin/bash
+
+CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+
+${CURRENT_DIR}/app/build_application_resources.sh
+${CURRENT_DIR}/ui/build_ui_resources.sh
+
diff --git a/resources/ui/build_ui_resources.sh b/resources/ui/build_ui_resources.sh
new file mode 100755
index 0000000..4f365ed
--- /dev/null
+++ b/resources/ui/build_ui_resources.sh
@@ -0,0 +1,110 @@
+#! /bin/bash
+
+# Check dependencies
+command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; }
+command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; }
+command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
+
+CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
+ASSETS_DIR="${BASE_DIR}/assets"
+
+OPTIPNG_OPTIONS="-preserve -quiet -o7"
+ICON_SIZE=192
+
+#######################################################
+
+# Game images (svg files found in `images` folder)
+AVAILABLE_GAME_IMAGES=""
+if [ -d "${CURRENT_DIR}/images" ]; then
+  AVAILABLE_GAME_IMAGES="$(find "${CURRENT_DIR}/images" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort)"
+fi
+
+# Skins (subfolders found in `skins` folder)
+AVAILABLE_SKINS=""
+if [ -d "${CURRENT_DIR}/skins" ]; then
+  AVAILABLE_SKINS="$(find "${CURRENT_DIR}/skins" -mindepth 1 -type d | awk -F/ '{print $NF}')"
+fi
+
+# Images per skin (svg files found recursively in `skins` folder and subfolders)
+SKIN_IMAGES=""
+if [ -d "${CURRENT_DIR}/skins" ]; then
+  SKIN_IMAGES="$(find "${CURRENT_DIR}/skins" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort | uniq)"
+fi
+
+#######################################################
+
+# optimize svg
+function optimize_svg() {
+  SOURCE="$1"
+
+  cp ${SOURCE} ${SOURCE}.tmp
+  scour \
+      --remove-descriptive-elements \
+      --enable-id-stripping \
+      --enable-viewboxing \
+      --enable-comment-stripping \
+      --nindent=4 \
+      --quiet \
+      -i ${SOURCE}.tmp \
+      -o ${SOURCE}
+  rm ${SOURCE}.tmp
+}
+
+# build icons
+function build_image() {
+  SOURCE="$1"
+  TARGET="$2"
+
+  echo "Building ${TARGET}"
+
+  if [ ! -f "${SOURCE}" ]; then
+    echo "Missing file: ${SOURCE}"
+    exit 1
+  fi
+
+  optimize_svg "${SOURCE}"
+
+  mkdir -p "$(dirname "${TARGET}")"
+
+  inkscape \
+      --export-width=${ICON_SIZE} \
+      --export-height=${ICON_SIZE} \
+      --export-filename=${TARGET} \
+      "${SOURCE}"
+
+  optipng ${OPTIPNG_OPTIONS} "${TARGET}"
+}
+
+function build_image_for_skin() {
+  SKIN_CODE="$1"
+
+  # skin images
+  for SKIN_IMAGE in ${SKIN_IMAGES}
+  do
+    build_image ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png
+  done
+}
+
+#######################################################
+
+# Delete existing generated images
+if [ -d "${ASSETS_DIR}/ui" ]; then
+  find ${ASSETS_DIR}/ui -type f -name "*.png" -delete
+fi
+if [ -d "${ASSETS_DIR}/skins" ]; then
+  find ${ASSETS_DIR}/skins -type f -name "*.png" -delete
+fi
+
+# build game images
+for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES}
+do
+  build_image ${CURRENT_DIR}/images/${GAME_IMAGE}.svg ${ASSETS_DIR}/ui/${GAME_IMAGE}.png
+done
+
+# build skins images
+for SKIN in ${AVAILABLE_SKINS}
+do
+  build_image_for_skin "${SKIN}"
+done
+
diff --git a/icons/button_back.svg b/resources/ui/images/button_back.svg
similarity index 100%
rename from icons/button_back.svg
rename to resources/ui/images/button_back.svg
diff --git a/icons/button_delete_saved_game.svg b/resources/ui/images/button_delete_saved_game.svg
similarity index 100%
rename from icons/button_delete_saved_game.svg
rename to resources/ui/images/button_delete_saved_game.svg
diff --git a/icons/button_resume_game.svg b/resources/ui/images/button_resume_game.svg
similarity index 100%
rename from icons/button_resume_game.svg
rename to resources/ui/images/button_resume_game.svg
diff --git a/icons/button_start.svg b/resources/ui/images/button_start.svg
similarity index 100%
rename from icons/button_start.svg
rename to resources/ui/images/button_start.svg
diff --git a/icons/game_fail.svg b/resources/ui/images/game_fail.svg
similarity index 100%
rename from icons/game_fail.svg
rename to resources/ui/images/game_fail.svg
diff --git a/icons/game_win.svg b/resources/ui/images/game_win.svg
similarity index 100%
rename from icons/game_win.svg
rename to resources/ui/images/game_win.svg
diff --git a/icons/placeholder.svg b/resources/ui/images/placeholder.svg
similarity index 100%
rename from icons/placeholder.svg
rename to resources/ui/images/placeholder.svg
-- 
GitLab