mastermind_rs/
lib.rs

1mod logic;
2mod util;
3use cursive::{
4    event::Key,
5    theme::{BaseColor, Color},
6    view::{Nameable, Resizable, ScrollStrategy},
7    views::{
8        Button, Dialog, DummyView, EditView, LinearLayout, ListView, OnEventView, Panel,
9        ScrollView, TextView,
10    },
11    Cursive,
12};
13
14// Spawn menu/main window
15pub fn menu(s: &mut Cursive) {
16    s.add_global_callback(Key::Esc, |s| quit_window(s));
17    s.add_global_callback('q', |s| {
18        s.pop_layer();
19    });
20    s.add_layer(
21        OnEventView::new(
22            Dialog::around(
23                LinearLayout::vertical()
24                    .child(TextView::new("Game settings").style(Color::Dark(BaseColor::Blue)))
25                    .child(DummyView)
26                    .child(util::setting_digit_num("Digit number:     "))
27                    .child(util::setting_pass_len("Code length:      "))
28                    .child(DummyView)
29                    .child(Button::new("Start game", game)),
30            )
31            .title("mastermind-rs")
32            .button("Rules", rules)
33            .button("About", about)
34            .button("Quit", |s| s.quit()),
35        )
36        .on_event('q', |s| s.quit())
37        .on_event(Key::Esc, |s| s.quit()),
38    );
39}
40
41// Spawn about page
42fn about(s: &mut Cursive) {
43    s.add_layer(
44        Dialog::around(
45            LinearLayout::vertical()
46                .child(util::banner())
47                .child(
48                    TextView::new(
49                        "
50A little game written for the sake of experience in writing
51Rust code. Also my first project using any kind of User Interface.
52                        ",
53                    )
54                    .style(Color::Dark(BaseColor::Blue)),
55                )
56                .child(util::source_button(
57                    "https://github.com/hiimsergey/mastermind-rs",
58                ))
59                .child(
60                    TextView::new(
61                        "
62Utilizes the \"cursive\" crate for building TUIs.
63                        ",
64                    )
65                    .style(Color::Dark(BaseColor::Blue)),
66                )
67                .child(util::source_button("https://crates.io/crates/cursive"))
68                .child(
69                    TextView::new(
70                        "
71v. 0.2.10 - GPLv3 License ",
72                    )
73                    .style(Color::Dark(BaseColor::Blue)),
74                ),
75        )
76        .button("Ok", |s| {
77            s.pop_layer();
78        })
79        .title("About mastermind-rs")
80        .fixed_width(64),
81    );
82}
83
84// Start game and spawn game window
85fn game(s: &mut Cursive) {
86    let digit_num: u8 = s
87        .call_on_name("digit_num", |v: &mut TextView| {
88            let binding = v.get_content();
89            binding.source().parse().unwrap()
90        })
91        .unwrap();
92    let pass_len: u8 = s
93        .call_on_name("pass_len", |v: &mut TextView| {
94            let binding = v.get_content();
95            binding.source().parse().unwrap()
96        })
97        .unwrap();
98    let code = logic::gen_code(digit_num, pass_len);
99
100    let settings = TextView::new(format!(
101        "
102Digit number:    {digit_num}
103Code length:     {pass_len}"
104    ))
105    .style(Color::Dark(BaseColor::Blue));
106    let input = LinearLayout::horizontal()
107        .child(TextView::new("Your guess: "))
108        .child(
109            EditView::new()
110                .on_submit(move |s, name: &str| {
111                    logic::check_guess(s, name, digit_num, &code);
112                })
113                .max_content_width(pass_len as usize)
114                .with_name("input")
115                .fixed_width(pass_len as usize + 1),
116        );
117
118    let list = Panel::new(ScrollView::new(ListView::new()).scroll_strategy(ScrollStrategy::StickToBottom).with_name("list"))
119        .fixed_height(12)
120        .fixed_width(2 * (pass_len as usize) + 14);
121    let game_sidebar = LinearLayout::vertical()
122        .child(settings)
123        .child(DummyView)
124        .child(input)
125        .child(DummyView.fixed_height(4))
126        .child(
127            Button::new("Menu", |s| {
128                s.pop_layer();
129            }),
130        )
131        .child(Button::new("Ragequit", |s| s.quit()));
132
133    s.add_layer(
134        Dialog::around(
135            LinearLayout::horizontal()
136                .child(list)
137                .child(DummyView)
138                .child(game_sidebar),
139        )
140        .title("Game"),
141    );
142}
143
144// Spawn confirmation window when pressing Esc
145fn quit_window(s: &mut Cursive) {
146    s.add_layer(
147        OnEventView::new(
148            Dialog::around(
149                TextView::new("Do you want to \nquit the game?").style(Color::Dark(BaseColor::Red)),
150            )
151            .title("Confirm quit")
152            .button("No", |s| {
153                s.pop_layer();
154            })
155            .button("Yes", |s| s.quit()),
156        )
157        .on_event(Key::Esc, |s| {
158            s.pop_layer();
159        })
160        .on_event('q', |s| {
161            s.pop_layer();
162        }),
163    );
164}
165
166// Spawn rules page
167fn rules(s: &mut Cursive) {
168    s.add_layer(
169        Dialog::around(
170            LinearLayout::vertical()
171                .child(
172                    TextView::new(
173                        "Use the arrow keys or the mouse to navigate.
174Press q to close windows and Esc to quit the game.",
175                    ),
176                )
177                .child(util::rules()),
178        )
179        .title("Rules")
180        .button("Ok", |s| {
181            s.pop_layer();
182        }),
183    );
184}