Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ impl StartMenuState {
fn with_selected(selected_index: usize) -> Self {
let items = vec![
("easy (4 bits)".to_string(), Bits::Four),
("easy Two's complement (4 bits)".to_string(), Bits::FourTwosComplement),
("easy+16 (4 bits*16)".to_string(), Bits::FourShift4),
("easy+256 (4 bits*256)".to_string(), Bits::FourShift8),
("easy+4096 (4 bits*4096)".to_string(), Bits::FourShift12),
Expand Down
80 changes: 68 additions & 12 deletions src/binary_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,13 @@ impl BinaryNumbersPuzzle {

Block::bordered().border_type(border_type).fg(border_color).render(area, buf);

let suggestion_str = format!("{suggestion}");
let suggestion_str = if self.bits.is_twos_complement() {
// Convert raw bit pattern to signed value for display
let signed_val = self.bits.raw_to_signed(*suggestion);
format!("{signed_val}")
} else {
format!("{suggestion}")
};

#[allow(clippy::cast_possible_truncation)]
Paragraph::new(suggestion_str.to_string())
Expand Down Expand Up @@ -642,6 +648,7 @@ enum GuessResult {
#[derive(Clone)]
pub enum Bits {
Four,
FourTwosComplement,
FourShift4,
FourShift8,
FourShift12,
Expand All @@ -653,7 +660,11 @@ pub enum Bits {
impl Bits {
pub const fn to_int(&self) -> u32 {
match self {
Self::Four | Self::FourShift4 | Self::FourShift8 | Self::FourShift12 => 4,
Self::Four
| Self::FourShift4
| Self::FourShift8
| Self::FourShift12
| Self::FourTwosComplement => 4,
Self::Eight => 8,
Self::Twelve => 12,
Self::Sixteen => 16,
Expand All @@ -662,6 +673,7 @@ impl Bits {
pub const fn scale_factor(&self) -> u32 {
match self {
Self::Four => 1,
Self::FourTwosComplement => 1,
Self::FourShift4 => 16,
Self::FourShift8 => 256,
Self::FourShift12 => 4096,
Expand All @@ -673,6 +685,7 @@ impl Bits {
pub const fn high_score_key(&self) -> u32 {
match self {
Self::Four => 4,
Self::FourTwosComplement => 42, // separate key for two's complement
Self::FourShift4 => 44,
Self::FourShift8 => 48,
Self::FourShift12 => 412,
Expand All @@ -686,7 +699,11 @@ impl Bits {
}
pub const fn suggestion_count(&self) -> usize {
match self {
Self::Four | Self::FourShift4 | Self::FourShift8 | Self::FourShift12 => 3,
Self::Four
| Self::FourShift4
| Self::FourShift8
| Self::FourShift12
| Self::FourTwosComplement => 3,
Self::Eight => 4,
Self::Twelve => 5,
Self::Sixteen => 6,
Expand All @@ -695,6 +712,7 @@ impl Bits {
pub const fn label(&self) -> &'static str {
match self {
Self::Four => "4 bits",
Self::FourTwosComplement => "4 bits (Two's complement)",
Self::FourShift4 => "4 bits*16",
Self::FourShift8 => "4 bits*256",
Self::FourShift12 => "4 bits*4096",
Expand All @@ -703,6 +721,21 @@ impl Bits {
Self::Sixteen => "16 bits",
}
}

/// Convert raw bit pattern to signed value for two's complement mode
pub const fn raw_to_signed(&self, raw: u32) -> i32 {
match self {
Self::FourTwosComplement => {
// 4-bit two's complement: range -8 to +7
if raw >= 8 { (raw as i32) - 16 } else { raw as i32 }
},
_ => raw as i32, // other modes use unsigned
}
}

pub const fn is_twos_complement(&self) -> bool {
matches!(self, Self::FourTwosComplement)
}
Comment on lines +736 to +738
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new is_twos_complement helper method lacks test coverage. Consider adding a test to verify it returns true only for FourTwosComplement and false for all other bit modes.

Copilot uses AI. Check for mistakes.
}

pub struct BinaryNumbersPuzzle {
Expand All @@ -725,21 +758,44 @@ impl BinaryNumbersPuzzle {

let mut suggestions = Vec::new();
let scale = bits.scale_factor();
while suggestions.len() < bits.suggestion_count() {
let raw = rng.random_range(0..u32::pow(2, bits.to_int()));
let num = raw * scale;
if !suggestions.contains(&num) {
suggestions.push(num);

if bits.is_twos_complement() {
// For two's complement, generate unique raw bit patterns (0-15)
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Comment states range is (0-15) but for 4-bit values the range should be (0..16), which generates values 0-15 inclusive. The comment is technically correct but could be clearer by stating '0 to 15 inclusive' or matching the Rust range syntax '0..16'.

Suggested change
// For two's complement, generate unique raw bit patterns (0-15)
// For two's complement, generate unique raw bit patterns in the range 0..16 (i.e., 0 to 15 inclusive)

Copilot uses AI. Check for mistakes.
let mut raw_values: Vec<u32> = Vec::new();
while raw_values.len() < bits.suggestion_count() {
let raw = rng.random_range(0..u32::pow(2, bits.to_int()));
if !raw_values.contains(&raw) {
raw_values.push(raw);
}
}
// Store raw bit patterns directly
suggestions = raw_values;
} else {
// For unsigned modes
while suggestions.len() < bits.suggestion_count() {
let raw = rng.random_range(0..u32::pow(2, bits.to_int()));
let num = raw * scale;
if !suggestions.contains(&num) {
suggestions.push(num);
}
}
Comment on lines +762 to 781
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The two's complement branch duplicates the same logic pattern as the else branch (lines 771-777) with only the scaling difference. Consider refactoring to eliminate duplication, such as: generate raw_values first, then apply scaling conditionally when building suggestions.

Suggested change
if bits.is_twos_complement() {
// For two's complement, generate unique raw bit patterns (0-15)
let mut raw_values: Vec<u32> = Vec::new();
while raw_values.len() < bits.suggestion_count() {
let raw = rng.random_range(0..u32::pow(2, bits.to_int()));
if !raw_values.contains(&raw) {
raw_values.push(raw);
}
}
// Store raw bit patterns directly
suggestions = raw_values;
} else {
// For unsigned modes
while suggestions.len() < bits.suggestion_count() {
let raw = rng.random_range(0..u32::pow(2, bits.to_int()));
let num = raw * scale;
if !suggestions.contains(&num) {
suggestions.push(num);
}
}
// Generate unique raw bit patterns for suggestions
let mut raw_values: Vec<u32> = Vec::new();
while raw_values.len() < bits.suggestion_count() {
let raw = rng.random_range(0..u32::pow(2, bits.to_int()));
if !raw_values.contains(&raw) {
raw_values.push(raw);
}
}
// Apply scaling if not two's complement
if bits.is_twos_complement() {
suggestions = raw_values.clone();
} else {
suggestions = raw_values.iter().map(|raw| raw * scale).collect();

Copilot uses AI. Check for mistakes.
}

let current_number = suggestions[0]; // scaled value
let raw_current_number = current_number / scale; // back-calculate raw bits
let current_number = suggestions[0]; // scaled value or raw for twos complement
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'twos' to 'two's' in comment for consistency with the mode name.

Suggested change
let current_number = suggestions[0]; // scaled value or raw for twos complement
let current_number = suggestions[0]; // scaled value or raw for two's complement

Copilot uses AI. Check for mistakes.
let raw_current_number = if bits.is_twos_complement() {
current_number // for two's complement, it's already the raw bit pattern
} else {
current_number / scale // back-calculate raw bits
};
suggestions.shuffle(&mut rng);

// Base time by bits + difficulty scaling (shorter as streak increases)
let base_time = match bits {
Bits::Four | Bits::FourShift4 | Bits::FourShift8 | Bits::FourShift12 => 8.0,
Bits::Four
| Bits::FourShift4
| Bits::FourShift8
| Bits::FourShift12
| Bits::FourTwosComplement => 8.0,
Bits::Eight => 12.0,
Bits::Twelve => 16.0,
Bits::Sixteen => 20.0,
Expand Down Expand Up @@ -868,7 +924,7 @@ impl HighScores {

fn save(&self) -> std::io::Result<()> {
let mut data = String::new();
for key in [4u32, 44u32, 48u32, 412u32, 8u32, 12u32, 16u32] {
for key in [4u32, 42u32, 44u32, 48u32, 412u32, 8u32, 12u32, 16u32] {
let val = self.get(key);
let _ = writeln!(data, "{key}={val}");
}
Expand Down