diff --git a/Cargo.lock b/Cargo.lock index ca93c6d..6e4a9b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,12 +81,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -277,26 +271,11 @@ dependencies = [ "bincode", "derive_more", "iced", - "iced_aw", "itertools", "once_cell", "serde", ] -[[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets 0.48.5", -] - [[package]] name = "clipboard-win" version = "4.5.0" @@ -476,15 +455,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "deranged" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" -dependencies = [ - "powerfmt", -] - [[package]] name = "derive_more" version = "0.99.17" @@ -890,29 +860,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "iana-time-zone" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "iced" version = "0.10.0" @@ -927,19 +874,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "iced_aw" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c51860ce7be5be6f6104c4e13b14e56662ebbd7c96c50e10069d59f8c3d892" -dependencies = [ - "chrono", - "iced_widget", - "num-traits", - "once_cell", - "time", -] - [[package]] name = "iced_core" version = "0.10.0" @@ -1274,9 +1208,9 @@ dependencies = [ [[package]] name = "lyon_algorithms" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a0349cd8f0270781bb93a824b63df6178e3b4a27794e7be3ce3763f5a44d6e" +checksum = "a3bca95f9a4955b3e4a821fbbcd5edfbd9be2a9a50bb5758173e5358bfb4c623" dependencies = [ "lyon_path", "num-traits", @@ -1284,9 +1218,9 @@ dependencies = [ [[package]] name = "lyon_geom" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad" +checksum = "edecfb8d234a2b0be031ab02ebcdd9f3b9ee418fb35e265f7a540a48d197bff9" dependencies = [ "arrayvec", "euclid", @@ -1305,13 +1239,13 @@ dependencies = [ [[package]] name = "lyon_tessellation" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bcac20d47825850fabf1e869bf7c2bbe2daefa0776c3cd2eb7cb74635f6e4a" +checksum = "8c7c67b5bc8123b352b2e7e742b47d1f236a13fe77619433be9568fbd888e9c0" dependencies = [ "float_next_after", "lyon_path", - "thiserror", + "num-traits", ] [[package]] @@ -1579,15 +1513,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "objc" version = "0.2.7" @@ -1836,12 +1761,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2258,26 +2177,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "time" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" -dependencies = [ - "deranged", - "libc", - "num_threads", - "powerfmt", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - [[package]] name = "tiny-skia" version = "0.8.4" @@ -2845,15 +2744,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 344a3d4..b7adaec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "checkers" authors = ["Сырцев Вадим Игоревич "] -description = "Шашки с системой контроля версий" +description = "Игра Шашки" version = "0.1.0" edition = "2021" @@ -10,8 +10,7 @@ edition = "2021" [dependencies] bincode = "1.3.3" derive_more = "0.99.17" -iced = "0.10.0" -iced_aw = { version = "0.7.0", features = ["menu", "modal"] } +iced = { version = "0.10.0", features = ["canvas"] } itertools = "0.12.0" once_cell = "1.18.0" serde = { version = "1.0.193", features = ["derive"] } diff --git a/README.md b/README.md index 63b2c46..e6d1574 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ [![Разработчик](https://img.shields.io/badge/%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA-%D0%A1%D1%8B%D1%80%D1%86%D0%B5%D0%B2%20%D0%92%D0%B0%D0%B4%D0%B8%D0%BC%20%D0%98%D0%B3%D0%BE%D1%80%D0%B5%D0%B2%D0%B8%D1%87-9933FF?labelColor=4c0099&style=flat&link=https://github.com/iced-rs/iced_aw)](https://github.com/iced-rs/iced_aw) [![build](https://github.com/syrtcevvi/checkers/actions/workflows/rust.yml/badge.svg)](https://github.com/syrtcevvi/checkers/actions/workflows/rust.yml) [![iced](https://img.shields.io/badge/iced-%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0%20GUI-blue?labelColor=00008B&style=flat&link=https://github.com/iced-rs/iced)](https://github.com/iced-rs/iced) -[![iced_aw](https://img.shields.io/badge/iced_aw-%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B8%20GUI-blue?labelColor=00008B&style=flat&link=https://github.com/iced-rs/iced_aw)](https://github.com/iced-rs/iced_aw) [![serde](https://img.shields.io/badge/serde-%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0%20%D1%81%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8-orange?labelColor=yellow&style=flat&link=https://github.com/serde-rs/serde)](https://github.com/serde-rs/serde) # Шашки с системой контроля версий (СКВ) @@ -18,7 +17,6 @@ - [Примеры работы](#примеры-работы) - [Управление](#управление) - [Взаимодействие с игровой доской](#взаимодействие-с-игровой-доской) - - [Взаимодействие с СКВ](#взаимодействие-с-скв) - [Правила](#правила) - [Зачем это было создано?](#зачем-это-было-создано) - [В чем польза ознакомления с проектом](#в-чем-польза-ознакомления-с-проектом) @@ -58,15 +56,6 @@ cargo run --release При выборе фигуры на доске **зелеными** клеточками подсвечиваются позиции, в которых может быть размещена данная фигура (*передвижение*), а **красными** клеточками - возможные взятия фигур. -### Взаимодействие с СКВ -В любой момент игрового процесса пользователь может создать снимок текущего состояния доски, с помощью меню верхнего уровня (СКВ). С каждым снимков связано некоторое сообщение, описывающее его. - -Ветка - обычный указатель на некоторый снимок. Если необходимо сделать развилку в процессе игры, это то, что нужно. - -После создания снимка, к нему можно вернуться, выбрав его в списке снимков для выбранной ветки. - -> В ближайшее время СКВ может быть устранена из программы - ## Правила ### Общие положения Данная программа не полностью следует всем общепринятым правилам шашек. Так, правило принудительного взятия фигур не реализовано. @@ -93,18 +82,19 @@ cargo run --release - все объекты СКВ в течение работы программы хранятся в оперативной памяти ЭВМ. - используется один файл для сохранения СКВ между перезапусками. При запуске программы происходит считывание, а перед закрытием - запись. +На данный момент СКВ удалена из программы. + На данном этапе польза от проекта чисто теоретическая ## В чем польза ознакомления с проектом? В данном проекте активно используется: - создание GUI с помощью iced, показан один из возможных подходов к разбиению сообщений (Messages) на подтипы -- graceful shutdown, позволяющий перехватить событие закрытия программы и провести некоторые дополнительные действия перед этим, в данном случае, записать в файл СКВ +- graceful shutdown, позволяющий перехватить событие закрытия программы и провести некоторые дополнительные действия перед этим (нет дополнительных действий) - работа с *shared ownership* (Rc), позволяющая изменять один и тот же участок памяти из разных структур - продвинутые концепции для работы с итераторами([itertools](https://docs.rs/itertools/latest/itertools/)), в частности, для формирования положений фигур и различных проверок по ходу игры ## Дальнейшие планы? На момент написания этого файла, планируется сделать следующие улучшения: -- Решить судьбу внедрённой СКВ (скорее всего, просто от нее избавиться) - Покрыть игровую логику автотестами - Оптимизировать использование программой оперативной памяти - Расширить доступный инструментарий программы diff --git a/src/application/app.rs b/src/application/app.rs index 9ebdf36..ba2d13c 100644 --- a/src/application/app.rs +++ b/src/application/app.rs @@ -17,156 +17,30 @@ limitations under the License. use std::{cell::RefCell, rc::Rc}; use iced::{ - event::Event, - executor, subscription, - widget::{button, column, container, pick_list, row, text_input, Text}, - window, Application, Command, Element, Renderer, Subscription, Theme, + event::Event, executor, subscription, widget::column, window, Application, Command, Element, + Renderer, Subscription, Theme, }; -use iced_aw::{helpers::menu_tree, menu_bar, menu_tree, modal, Card, MenuTree}; - use crate::application::{ - button_style::ButtonStyle, enums::Message, - io::persist_vcs_in_file, - structs::{ - Board, BoardMessage, CreationModal, CreationModalMessage, GameData, ModalType, Vcs, - VcsMessage, - }, + structs::{Board, BoardMessage, GameData}, }; -use super::io; - pub struct Checkers { /// Игральная доска board: Board, /// Данные о состоянии игры game_data: Rc>, - /// Система контроля версий - vcs: Vcs, - /// Модальное окно создания - creation_modal: Option, } -impl Checkers { - const MODAL_WINDOW_WIDTH: f32 = 420.0; - - fn vcs_controls<'a>(&self) -> Element<'a, Message> { - row![ - column![ - Text::new("Выбор ветки"), - pick_list( - self.vcs.branch_names(), - Some(self.vcs.current_branch_name()), - |branch_name: String| Message::Vcs(VcsMessage::SwitchToBranch(branch_name)) - ) - ], - column![ - Text::new("Выбор снимка"), - pick_list( - self.vcs.commit_headers(), - self.vcs.current_commit_header(), - |commit_header: String| { - let id: isize = commit_header.split_once("-").unwrap().0.parse().unwrap(); - Message::Vcs(VcsMessage::SwitchToCommit(id)) - } - ) - ], - ] - .padding(8) - .spacing(20) - .into() - } - - /// Создаёт кнопку для меню верхнего уровня - fn menubar_button<'a>( - content: impl Into>, - ) -> button::Button<'a, Message, Renderer> { - button(content) - .padding([4, 8]) - .style(iced::theme::Button::Custom(Box::new(ButtonStyle {}))) - .on_press(Message::None) - } - - /// Создаёт кнопку для вложенного меню - fn menu_button<'a>( - content: impl Into>, - message: Option, - ) -> button::Button<'a, Message, Renderer> { - let btn = button(content) - .padding([4, 8]) - .width(200) - .style(iced::theme::Button::Custom(Box::new(ButtonStyle {}))); - - if let Some(message) = message { - btn.on_press(message) - } else { - btn +impl Default for Checkers { + fn default() -> Self { + let game_data = Rc::new(RefCell::new(GameData::default())); + Self { + board: Board::new(game_data.clone()), + game_data, } } - - /// Создаёт элемент внутреннего меню - fn menu_item<'a>( - label: &'static str, - message: Option, - ) -> MenuTree<'a, Message, Renderer> { - menu_tree!(Self::menu_button(label, message)) - } - - /// Создаёт элемент меню версионного контроля - fn vcs_menu<'a>(&self) -> MenuTree<'a, Message, Renderer> { - menu_tree( - Self::menubar_button("СКВ"), - vec![ - // TODO Проверить условие того, что пользователю дозволено создать новый снимок - Self::menu_item( - "Создать снимок", - if self.vcs.is_commit_creation_allowed() { - Some(Message::CreationModal(CreationModalMessage::Open( - ModalType::CommitCreation, - ))) - } else { - None - }, - ), - Self::menu_item( - "Создать ветку", - Some(Message::CreationModal(CreationModalMessage::Open( - ModalType::BranchCreation, - ))), - ), - ], - ) - } - - fn creation_modal_overlay<'a>( - &self, - label: &'static str, - input_placeholder: &'static str, - ) -> Element<'a, Message, Renderer> { - let creation_modal = self.creation_modal.as_ref().unwrap(); - - Card::<_, Renderer>::new( - label, - row![text_input(input_placeholder, &creation_modal.input_value) - .on_input( - |value| Message::CreationModal(CreationModalMessage::TextInputChanged(value)) - ) - .on_submit(Message::CreationModal(CreationModalMessage::Finished( - creation_modal.input_value.clone() - )))], - ) - .max_width(Self::MODAL_WINDOW_WIDTH) - .into() - } - - fn commit_creation_modal_overlay<'a>(&self) -> Element<'a, Message, Renderer> { - self.creation_modal_overlay("Создание снимка", "Введите сообщение") - } - - fn branch_creation_modal_overlay<'a>(&self) -> Element<'a, Message, Renderer> { - self.creation_modal_overlay("Создание ветки", "Введите название") - } } impl Application for Checkers { @@ -176,32 +50,11 @@ impl Application for Checkers { type Flags = (); fn new(_flags: Self::Flags) -> (Self, Command) { - let vcs = if let Ok(vcs) = io::restore_vcs_from_file() { - vcs - } else { - Vcs::default() - }; - let game_data = Rc::new(RefCell::new( - if let Some(game_data) = vcs.get_current_state() { - game_data - } else { - GameData::default() - }, - )); - - ( - Self { - board: Board::new(game_data.clone()), - vcs, - game_data, - creation_modal: None, - }, - Command::none(), - ) + (Self::default(), Command::none()) } fn title(&self) -> String { - String::from("Шашки с системой контроля версий") + String::from(r#"Игра "Шашки""#) } fn update(&mut self, message: Self::Message) -> Command { @@ -246,60 +99,11 @@ impl Application for Checkers { } self.board.update(); } - Message::Vcs(vcs_message) => match vcs_message { - VcsMessage::SwitchToBranch(branch_name) => { - if let Some(game_data) = self.vcs.switch_to_branch(branch_name) { - self.game_data.replace(game_data); - // Перерисовываем доску - self.board.update(); - } - } - VcsMessage::SwitchToCommit(commit_id) => { - self.game_data.replace(self.vcs.switch_to_commit(commit_id)); - self.board.update(); - } - }, - Message::CreationModal(creation_modal_message) => match creation_modal_message { - CreationModalMessage::Open(modal_type) => { - if let None = self.creation_modal { - self.creation_modal = Some(CreationModal::new(modal_type)); - } - } - CreationModalMessage::TextInputChanged(value) => { - if let Some(creation_modal) = &mut self.creation_modal { - creation_modal.input_value = value; - } - } - CreationModalMessage::Close => { - self.creation_modal = None; - } - CreationModalMessage::Finished(value) => { - match self.creation_modal.as_ref().unwrap().modal_type { - ModalType::BranchCreation => { - self.vcs.create_branch(value); - } - ModalType::CommitCreation => { - self.vcs - .create_commit(value, self.game_data.borrow().clone()); - } - } - self.creation_modal = None; - } - }, Message::EventOccured(event) => { - if let Event::Window(event) = event { - if let window::Event::CloseRequested = event { - match persist_vcs_in_file(&self.vcs) { - Ok(_) => println!("СКВ успешно была записана в файл!"), - Err(err) => { - println!("Возникла ошибка во время записи СКВ в файл: {err}") - } - }; - return window::close(); - } + if let Event::Window(window::Event::CloseRequested) = event { + return window::close(); } } - Message::None => {} } Command::none() } @@ -309,23 +113,6 @@ impl Application for Checkers { } fn view(&self) -> Element<'_, Self::Message, Renderer> { - let underlay = container(column![ - menu_bar!(self.vcs_menu()), - self.board.view().map(Message::Board), - self.vcs_controls() - ]); - - let overlay = - self.creation_modal - .as_ref() - .map(|creation_modal| match creation_modal.modal_type { - ModalType::BranchCreation => self.branch_creation_modal_overlay(), - ModalType::CommitCreation => self.commit_creation_modal_overlay(), - }); - - modal(underlay, overlay) - .on_esc(Message::CreationModal(CreationModalMessage::Close)) - .backdrop(Message::CreationModal(CreationModalMessage::Close)) - .into() + column![self.board.view().map(Message::Board)].into() } } diff --git a/src/application/button_style.rs b/src/application/button_style.rs deleted file mode 100644 index 34cd332..0000000 --- a/src/application/button_style.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use iced::{widget::button, Color}; - -pub struct ButtonStyle; - -impl button::StyleSheet for ButtonStyle { - type Style = iced::Theme; - - fn active(&self, style: &Self::Style) -> button::Appearance { - button::Appearance { - text_color: style.extended_palette().background.base.text, - border_radius: [4.0; 4].into(), - background: Some(Color::TRANSPARENT.into()), - ..Default::default() - } - } - - fn hovered(&self, style: &Self::Style) -> button::Appearance { - let plt = style.extended_palette(); - - button::Appearance { - background: Some(plt.primary.weak.color.into()), - text_color: plt.primary.weak.text, - ..self.active(style) - } - } -} diff --git a/src/application/enums/message.rs b/src/application/enums/message.rs index 7a0680d..6cc1931 100644 --- a/src/application/enums/message.rs +++ b/src/application/enums/message.rs @@ -16,13 +16,10 @@ limitations under the License. use iced::event; -use crate::application::structs::{BoardMessage, CreationModalMessage, VcsMessage}; +use crate::application::structs::BoardMessage; #[derive(Debug, Clone)] pub enum Message { Board(BoardMessage), - Vcs(VcsMessage), - CreationModal(CreationModalMessage), EventOccured(event::Event), - None, } diff --git a/src/application/enums/route.rs b/src/application/enums/route.rs index 4194e38..6ca2d4c 100644 --- a/src/application/enums/route.rs +++ b/src/application/enums/route.rs @@ -31,8 +31,8 @@ impl Route { /// Возвращает конечную позицию фигуры pub fn position(&self) -> Position { match self { - Self::Movement(position) => position.clone(), - Self::Taking(position, ..) => position.clone(), + Self::Movement(position) => *position, + Self::Taking(position, ..) => *position, } } } diff --git a/src/application/io.rs b/src/application/io.rs index d75cf4e..4442ef9 100644 --- a/src/application/io.rs +++ b/src/application/io.rs @@ -14,30 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -use crate::application::structs::Vcs; use serde::Serialize; use std::{ fs::OpenOptions, - io::{BufReader, Result, Write}, + io::{Result, Write}, path::Path, }; -/// Путь к файлу, в котором будет сохранено состояние СКВ -const VCS_FILE_PATH: &str = "vcs.data"; - -/// Восстанавливает из файла состояние СКВ -pub fn restore_vcs_from_file() -> Result { - let file = OpenOptions::new().read(true).open(VCS_FILE_PATH)?; - let reader = BufReader::new(file); - - Ok(bincode::deserialize_from(reader).unwrap()) -} - -/// Записывает сериализованный объект СКВ в файл -pub fn persist_vcs_in_file(vcs: &Vcs) -> Result<()> { - persist_in_file(VCS_FILE_PATH, vcs) -} - /// Записывает сериализуемый объект в выходной файл fn persist_in_file(path: impl AsRef, value: &impl Serialize) -> Result<()> { let mut file = OpenOptions::new() diff --git a/src/application/mod.rs b/src/application/mod.rs index f13e6a2..b332299 100644 --- a/src/application/mod.rs +++ b/src/application/mod.rs @@ -1,7 +1,6 @@ mod app; -mod button_style; mod enums; mod io; mod structs; -pub use self::app::Checkers; +pub use app::Checkers; diff --git a/src/application/structs/board/board.rs b/src/application/structs/board/board.rs index 211608d..e0b4fbc 100644 --- a/src/application/structs/board/board.rs +++ b/src/application/structs/board/board.rs @@ -224,10 +224,7 @@ impl Program for Board { let overlay = { let mut frame = Frame::new(renderer, bounds.size()); - if let Some(position) = cursor - .position_in(bounds) - .map(|point| Self::get_cell_position(point)) - { + if let Some(position) = cursor.position_in(bounds).map(Self::get_cell_position) { // Если пользователь указывает на одну из ячеек игральной доски if game_data.is_inside_board(position) { // Подсвечиваем ячейку доски, над которой находится курсор пользователя @@ -363,7 +360,7 @@ impl Program for Board { { *state = State::None; match route { - Route::Movement(position) => { + Route::Movement(_position) => { return ( Status::Captured, Some(Message::MovePiece { @@ -373,7 +370,7 @@ impl Program for Board { }), ); } - Route::Taking(position, taken_pieces_positions) => { + Route::Taking(_position, taken_pieces_positions) => { return ( Status::Captured, Some(Message::TakePieces { @@ -396,9 +393,9 @@ impl Program for Board { fn mouse_interaction( &self, - state: &Self::State, - bounds: Rectangle, - cursor: mouse::Cursor, + _state: &Self::State, + _bounds: Rectangle, + _cursor: mouse::Cursor, ) -> mouse::Interaction { mouse::Interaction::Pointer } diff --git a/src/application/structs/creation_modal/message.rs b/src/application/structs/creation_modal/message.rs deleted file mode 100644 index bda0474..0000000 --- a/src/application/structs/creation_modal/message.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use super::ModalType; - -#[derive(Debug, Clone)] -pub enum Message { - Open(ModalType), - Close, - TextInputChanged(String), - Finished(String), -} diff --git a/src/application/structs/creation_modal/mod.rs b/src/application/structs/creation_modal/mod.rs deleted file mode 100644 index 3b0528a..0000000 --- a/src/application/structs/creation_modal/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -mod message; -mod modal_type; - -pub use self::{message::Message, modal_type::ModalType}; - -/// Модальный диалог создания чего-либо -pub struct CreationModal { - /// Значение, записанное пользователем в поле ввода модального окна - pub input_value: String, - /// Тип модального диалога создания объекта - pub modal_type: ModalType, -} - -impl CreationModal { - const DEFAULT_INPUT_VALUE_CAPACITY: usize = 32; - - pub fn new(modal_type: ModalType) -> Self { - Self { - input_value: String::with_capacity(Self::DEFAULT_INPUT_VALUE_CAPACITY), - modal_type, - } - } -} diff --git a/src/application/structs/creation_modal/modal_type.rs b/src/application/structs/creation_modal/modal_type.rs deleted file mode 100644 index 44dea51..0000000 --- a/src/application/structs/creation_modal/modal_type.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/// Тип модального диалога -#[derive(Debug, Copy, Clone)] -pub enum ModalType { - CommitCreation, - BranchCreation, -} diff --git a/src/application/structs/game_data.rs b/src/application/structs/game_data.rs index 856051e..2bd9011 100644 --- a/src/application/structs/game_data.rs +++ b/src/application/structs/game_data.rs @@ -24,6 +24,7 @@ use crate::application::{ #[derive(Debug, Clone, Deserialize, Serialize)] pub struct GameData { + // TODO заменить HashMap на что-то более эффективное по памяти pub white_pieces: HashMap, pub black_pieces: HashMap, /// Текущий ход стороны @@ -64,7 +65,7 @@ impl GameData { /// Проверяет, закончена ли игра pub fn is_game_ended(&self) -> bool { - self.white_pieces.len() == 0 || self.black_pieces.len() == 0 + self.white_pieces.is_empty() || self.black_pieces.is_empty() } /// Проверяет, выполнено ли для фигуры условие того, что она превращается в дамку @@ -197,7 +198,7 @@ impl GameData { fn get_taking_routes(&self, position: Position, piece: Piece, side: Side) -> Vec { let mut taking_routes: Vec = Vec::with_capacity(16); - self.get_taking_routes_rec(position, piece, side, &vec![], &mut taking_routes); + self.get_taking_routes_rec(position, piece, side, &[], &mut taking_routes); taking_routes } @@ -206,7 +207,7 @@ impl GameData { position: Position, piece: Piece, side: Side, - taken_pieces_positions: &Vec, + taken_pieces_positions: &[Position], taking_routes: &mut Vec, ) { let positions: Vec<(Position, Direction)> = match piece { @@ -250,7 +251,7 @@ impl GameData { // Позиции вражеских фигур, которые можно взять for (enemy_piece_position, direction) in positions { let result_position = enemy_piece_position.next_diagonal(direction); - let mut enemy_pieces_positions = taken_pieces_positions.clone(); + let mut enemy_pieces_positions = Vec::from(taken_pieces_positions); enemy_pieces_positions.push(enemy_piece_position); self.get_taking_routes_rec( result_position, diff --git a/src/application/structs/mod.rs b/src/application/structs/mod.rs index b3b835b..f0ea7a0 100644 --- a/src/application/structs/mod.rs +++ b/src/application/structs/mod.rs @@ -1,13 +1,9 @@ pub mod board; -mod creation_modal; mod game_data; mod position; -mod vcs; pub use self::{ board::{Board, Message as BoardMessage}, - creation_modal::{CreationModal, Message as CreationModalMessage, ModalType}, game_data::GameData, position::Position, - vcs::{Message as VcsMessage, Vcs}, }; diff --git a/src/application/structs/vcs/message.rs b/src/application/structs/vcs/message.rs deleted file mode 100644 index b7666c6..0000000 --- a/src/application/structs/vcs/message.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#[derive(Debug, Clone)] -pub enum Message { - /// Выбрать снимок по идентификатору - SwitchToCommit(isize), - /// Выбрать ветку по имени - SwitchToBranch(String), -} diff --git a/src/application/structs/vcs/mod.rs b/src/application/structs/vcs/mod.rs deleted file mode 100644 index f6c2960..0000000 --- a/src/application/structs/vcs/mod.rs +++ /dev/null @@ -1,184 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -mod message; -mod structs; -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -use crate::application::structs::GameData; -pub use message::Message; -use structs::{Branch, Commit}; - -/// Версионный контроль состояния доски игры "Шашки" -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Vcs { - current_branch_name: String, - current_commit_id: Option, - - /// Ветки, созданные пользователем. Имя ветки связано с объектом ветки - branches: HashMap, - /// Снимки, созданные пользователем. Hash снимка связан с объектом снимка - commits: HashMap, - /// Идентификатор, который будет присвоен следующему снимку - next_commit_id: isize, -} - -impl Vcs { - const DEFAULT_BRANCH_NAME: &str = "default"; - const DEFAULT_BRANCHES_CAPACITY: usize = 16; - const DEFAULT_COMMITS_CAPACITY: usize = 64; - - /// Возвращает состояние игры для текущего снимка, если такой был создан - pub fn get_current_state(&self) -> Option { - self.get_current_commit() - .map(|commit| commit.game_data.clone()) - } - - /// Создаёт новую ветку - pub fn create_branch(&mut self, name: String) { - let new_branch = Branch { - commit_id: self.current_commit_id, - name: name.clone(), - }; - self.branches.insert(name.clone(), new_branch); - self.current_branch_name = name; - } - - /// Проверяет, разрешено ли создание нового снимка - /// - /// Создание снимка в центре истории снимков запрещено - pub fn is_commit_creation_allowed(&self) -> bool { - /* - Создание снимков разрешено только если на текущий снимок указывает какая-либо из созданных - веток или если еще не было создано никаких снимков - */ - self.branches - .values() - .any(|branch| branch.commit_id == self.current_commit_id) - } - - /// Создаёт новый снимок - /// - /// Параметры: - /// message - поясняющее сообщение, которое будет храниться вместе со снимком - /// game_data - состояние игры, которые будут записаны в объект снимка - pub fn create_commit(&mut self, message: String, game_data: GameData) { - let id = self.next_commit_id; - let commit = Commit { - id, - parent_commit_id: self.current_commit_id, - message, - game_data, - }; - self.get_current_branch_mut().commit_id = Some(commit.id); - self.current_commit_id = Some(commit.id); - self.commits.insert(commit.id, commit); - - self.next_commit_id += 1; - } - - /// Возвращает имя выбранной ветки - pub fn current_branch_name(&self) -> String { - self.get_current_branch().name.clone() - } - - /// Возвращает имена существующих веток - pub fn branch_names(&self) -> Vec { - self.branches - .keys() - .map(|branch_name| branch_name.clone()) - .collect() - } - - /// Переключается на выбранный снимок - /// - /// Возвращает состояние игры на момент создания данного снимка - pub fn switch_to_commit(&mut self, id: isize) -> GameData { - self.current_commit_id = Some(id); - self.get_current_commit().unwrap().game_data.clone() - } - - /// Переключается на выбранную ветку - /// - /// Возвращает состояние игры. Состояние может отсутстовать, если пользователь не сделал ни одного снимка - pub fn switch_to_branch(&mut self, name: String) -> Option { - self.current_branch_name = name; - self.current_commit_id = self.get_current_branch().commit_id; - self.get_current_commit() - .map(|commit| commit.game_data.clone()) - } - - /// Возвращает заголовок текущего снимка - pub fn current_commit_header(&self) -> Option { - self.get_current_commit().map(|commit| commit.to_string()) - } - - pub fn commit_headers(&self) -> Vec { - let mut commit_headers = Vec::with_capacity(Self::DEFAULT_COMMITS_CAPACITY); - - // Строим цепочку коммитов, начиная с выбранного, заканчивая самым первым - if let Some(commit_id) = self.get_current_branch().commit_id { - self.get_commit_chain(&mut commit_headers, commit_id); - } - commit_headers - } - - fn get_commit_chain(&self, commit_headers: &mut Vec, commit_id: isize) { - let commit = self.get_commit(commit_id); - commit_headers.push(commit.to_string()); - if let Some(parent_commit_id) = commit.parent_commit_id { - self.get_commit_chain(commit_headers, parent_commit_id); - } - } - - /// Возвращает ссылку на неизменяемый объект снимка - fn get_commit(&self, commit_id: isize) -> &Commit { - self.commits.get(&commit_id).unwrap() - } - - /// Возвращает ссылку на неизменяемый объект текущего снимка - fn get_current_commit(&self) -> Option<&Commit> { - self.current_commit_id - .map(|commit_id| self.commits.get(&commit_id).unwrap()) - } - - /// Возвращает ссылку на неизменяемый объект текущей ветки - fn get_current_branch(&self) -> &Branch { - self.branches.get(&self.current_branch_name).unwrap() - } - - /// Возвращает ссылку на изменяемый объект текущей ветки - fn get_current_branch_mut(&mut self) -> &mut Branch { - self.branches.get_mut(&self.current_branch_name).unwrap() - } -} - -impl Default for Vcs { - fn default() -> Self { - let current_branch = Branch::new(Self::DEFAULT_BRANCH_NAME, None); - let mut branches = HashMap::with_capacity(Self::DEFAULT_BRANCHES_CAPACITY); - branches.insert(current_branch.name.clone(), current_branch.clone()); - Self { - current_branch_name: Self::DEFAULT_BRANCH_NAME.to_string(), - current_commit_id: None, - branches, - commits: HashMap::with_capacity(Self::DEFAULT_COMMITS_CAPACITY), - next_commit_id: isize::default(), - } - } -} diff --git a/src/application/structs/vcs/structs/branch.rs b/src/application/structs/vcs/structs/branch.rs deleted file mode 100644 index 1773349..0000000 --- a/src/application/structs/vcs/structs/branch.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use serde::{Deserialize, Serialize}; - -use derive_more::Display; - -#[derive(Debug, Display, Clone, Deserialize, Serialize)] -#[display(fmt = "{}", name)] -pub struct Branch { - /// Идентификатор снимка, на который указывает данная ветка - /// - /// Если коммит отсутствует, значит пользователь еще не сделал ни одного - pub commit_id: Option, - /// Название ветки - pub name: String, -} - -impl Branch { - pub fn new(name: &str, commit_id: Option) -> Self { - Self { - name: name.to_string(), - commit_id, - } - } -} diff --git a/src/application/structs/vcs/structs/commit.rs b/src/application/structs/vcs/structs/commit.rs deleted file mode 100644 index 2978fe3..0000000 --- a/src/application/structs/vcs/structs/commit.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2023 Сырцев Вадим Игоревич - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use serde::{Deserialize, Serialize}; - -use crate::application::structs::GameData; -use derive_more::Display; - -/// Снимок версионного контроля -#[derive(Debug, Display, Clone, Deserialize, Serialize)] -#[display(fmt = "{}-{}", id, message)] -pub struct Commit { - /// Идентификатор снимка - pub id: isize, - /// Идентификатор родительского снимка. Если данный снимок является первым в истории, у него нет - pub parent_commit_id: Option, - /// Сообщение, связанное с данным снимком - pub message: String, - /// Состояние игры, сохранённое в снимке - pub game_data: GameData, -} diff --git a/src/application/structs/vcs/structs/mod.rs b/src/application/structs/vcs/structs/mod.rs deleted file mode 100644 index 307d16b..0000000 --- a/src/application/structs/vcs/structs/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod branch; -mod commit; - -pub use self::{branch::Branch, commit::Commit};