Files
hakorune/src/boxes/random_box.rs
Moe Charm 0c786a8ac6 🔥 feat: Arc<Mutex> Revolution完全達成 - 全Box型統一実装
## 🎯 主要な変更
- ArrayBoxをArc<Mutex>パターンで完全再実装
- 全メソッドが`&self`で統一(push, pop, get, set等)
- Box<dyn NyashBox>引数対応でNyashから使用可能に

##  修正内容
- ArrayBox: 完全なArc<Mutex>実装に置き換え
- BufferBox: ArrayBoxとの連携修正、デバッグ出力削除
- StringBox: 新しいArrayBoxインポートに修正
- RandomBox: 新しいArrayBoxインポートに修正
- box_trait.rs: 古いArrayBox定義を削除しre-export追加

## 🧪 テスト追加
- test_buffer_box.nyash: BufferBox動作確認
- test_random_box.nyash: RandomBox動作確認
- test_new_boxes.nyash: 包括的Box機能テスト修正

##  確認済み動作
- ArrayBox: push/pop/get/set/join等全メソッド
- BufferBox: write/readAll/length
- RandomBox: choice/shuffle等配列操作
- JSONBox/RegexBox: 既に正しく実装済み

🚀 全Box型がArc<Mutex>パターンで統一完了!

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-10 16:46:39 +09:00

290 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*! 🎲 RandomBox - 乱数生成Box
*
* ## 📝 概要
* 高品質な乱数生成機能を提供するBox。
* ゲーム開発、統計処理、テストデータ生成に最適。
*
* ## 🛠️ 利用可能メソッド
*
* ### 🔢 基本乱数
* - `random()` - 0.01.0の浮動小数点乱数
* - `randInt(min, max)` - 指定範囲の整数乱数
* - `randBool()` - true/falseのランダム選択
* - `seed(value)` - 乱数種を設定(再現可能な乱数)
*
* ### 🎯 選択・配列操作
* - `choice(array)` - 配列からランダム選択
* - `shuffle(array)` - 配列をシャッフル
*
* ### 🎨 生成
* - `randString(length)` - ランダム文字列生成
* - `probability(prob)` - 指定確率でtrue
*
* ## 💡 使用例
* ```nyash
* local random, result, dice, array
* random = new RandomBox()
*
* // 基本的な乱数
* result = random.random() // 0.01.0
* dice = random.randInt(1, 6) // サイコロ(1-6)
* result = random.randBool() // true or false
*
* // 配列関連
* array = ["apple", "banana", "cherry"]
* result = random.choice(array) // ランダム選択
* array = random.shuffle(array) // シャッフル
*
* // ゲーム用途
* local password, critical_hit
* password = random.randString(8) // 8文字のランダム文字列
* critical_hit = random.probability(0.1) // 10%でクリティカル
* ```
*
* ## 🎮 実用例
* ```nyash
* // RPGダメージ計算
* local damage, is_critical
* damage = random.randInt(10, 20) // 基本ダメージ10-20
* is_critical = random.probability(0.15) // 15%でクリティカル
* if (is_critical) {
* damage = damage * 2
* }
*
* // テストデータ生成
* local users, user_id, user_name
* users = []
* loop(i < 10) {
* user_id = random.randInt(1000, 9999)
* user_name = "user_" + random.randString(5)
* users.push(user_name + ":" + user_id)
* }
* ```
*
* ## ⚠️ 注意
* - 暗号学的に安全な乱数ではない(セキュリティ用途非推奨)
* - seed()で同じ値を設定すると同じ乱数列を生成(テスト用)
* - 大きな配列のshuffleは処理時間が長い場合あり
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox};
use crate::boxes::array::ArrayBox;
use crate::boxes::math_box::FloatBox;
use std::fmt::{Debug, Display};
use std::any::Any;
use std::sync::{Arc, Mutex};
/// 乱数生成を提供するBox
#[derive(Debug, Clone)]
pub struct RandomBox {
// 簡易線形合同法による疑似乱数生成器
seed: Arc<Mutex<u64>>,
id: u64,
}
impl RandomBox {
pub fn new() -> Self {
static mut COUNTER: u64 = 0;
let id = unsafe {
COUNTER += 1;
COUNTER
};
// 現在時刻を種として使用
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
Self {
seed: Arc::new(Mutex::new(seed)),
id,
}
}
/// 種を設定
pub fn seed(&self, new_seed: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(int_box) = new_seed.as_any().downcast_ref::<IntegerBox>() {
*self.seed.lock().unwrap() = int_box.value as u64;
Box::new(StringBox::new("Seed set"))
} else {
Box::new(StringBox::new("Error: seed() requires integer input"))
}
}
/// 次の乱数を生成(線形合同法)
fn next_random(&self) -> u64 {
let mut seed = self.seed.lock().unwrap();
// 線形合同法の定数Numerical Recipes より)
*seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
*seed
}
/// 0.0-1.0の浮動小数点乱数
pub fn random(&self) -> Box<dyn NyashBox> {
let r = self.next_random();
let normalized = (r as f64) / (u64::MAX as f64);
Box::new(FloatBox::new(normalized))
}
/// 指定範囲の整数乱数
pub fn randInt(&self, min: Box<dyn NyashBox>, max: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let (Some(min_int), Some(max_int)) = (
min.as_any().downcast_ref::<IntegerBox>(),
max.as_any().downcast_ref::<IntegerBox>()
) {
if min_int.value > max_int.value {
return Box::new(StringBox::new("Error: min must be <= max"));
}
let range = (max_int.value - min_int.value + 1) as u64;
let r = self.next_random() % range;
Box::new(IntegerBox::new(min_int.value + r as i64))
} else {
Box::new(StringBox::new("Error: randInt() requires two integer inputs"))
}
}
/// true/falseのランダム選択
pub fn randBool(&self) -> Box<dyn NyashBox> {
let r = self.next_random();
Box::new(BoolBox::new(r % 2 == 0))
}
/// 配列からランダム選択
pub fn choice(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
if length == 0 {
return Box::new(StringBox::new("Error: cannot choose from empty array"));
}
let index = self.next_random() % (length as u64);
// 新しいArrayBox.get()は既にBox<dyn NyashBox>を返すので、直接使用
array_box.get(Box::new(IntegerBox::new(index as i64)))
} else {
Box::new(StringBox::new("Error: choice() requires array input"))
}
}
/// 配列をシャッフル
pub fn shuffle(&self, array: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(array_box) = array.as_any().downcast_ref::<ArrayBox>() {
let length = array_box.length().to_string_box().value.parse::<i64>().unwrap_or(0);
if length <= 1 {
return array;
}
// 新しい配列を作成
let shuffled = ArrayBox::new();
// 元の配列の要素を全て新しい配列にコピー
for i in 0..length {
let element = array_box.get(Box::new(IntegerBox::new(i as i64)));
// NullBoxでなければ追加
if element.type_name() != "NullBox" {
shuffled.push(element);
}
}
// 簡易シャッフル実装完全なFisher-Yatesは複雑なので
// 代わりに、元の配列からランダムに選んで新しい配列を作る
let result = ArrayBox::new();
let mut remaining_indices: Vec<usize> = (0..length as usize).collect();
while !remaining_indices.is_empty() {
let random_idx = (self.next_random() % remaining_indices.len() as u64) as usize;
let actual_idx = remaining_indices.remove(random_idx);
let element = array_box.get(Box::new(IntegerBox::new(actual_idx as i64)));
// NullBoxでなければ追加
if element.type_name() != "NullBox" {
result.push(element);
}
}
Box::new(result)
} else {
Box::new(StringBox::new("Error: shuffle() requires array input"))
}
}
/// ランダムな文字列生成
pub fn randString(&self, length: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(len_int) = length.as_any().downcast_ref::<IntegerBox>() {
if len_int.value < 0 {
return Box::new(StringBox::new("Error: length must be positive"));
}
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let char_vec: Vec<char> = chars.chars().collect();
let mut result = String::new();
for _ in 0..len_int.value {
let index = self.next_random() % (char_vec.len() as u64);
result.push(char_vec[index as usize]);
}
Box::new(StringBox::new(&result))
} else {
Box::new(StringBox::new("Error: randString() requires integer length"))
}
}
/// 指定確率でtrue
pub fn probability(&self, prob: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
if let Some(float_box) = prob.as_any().downcast_ref::<FloatBox>() {
if float_box.value < 0.0 || float_box.value > 1.0 {
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
}
let r = self.next_random() as f64 / u64::MAX as f64;
Box::new(BoolBox::new(r < float_box.value))
} else if let Some(int_box) = prob.as_any().downcast_ref::<IntegerBox>() {
let prob_val = int_box.value as f64;
if prob_val < 0.0 || prob_val > 1.0 {
return Box::new(StringBox::new("Error: probability must be 0.0-1.0"));
}
let r = self.next_random() as f64 / u64::MAX as f64;
Box::new(BoolBox::new(r < prob_val))
} else {
Box::new(StringBox::new("Error: probability() requires numeric input"))
}
}
}
impl NyashBox for RandomBox {
fn type_name(&self) -> &'static str {
"RandomBox"
}
fn to_string_box(&self) -> StringBox {
StringBox::new("RandomBox()")
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_random) = other.as_any().downcast_ref::<RandomBox>() {
BoolBox::new(self.id == other_random.id)
} else {
BoolBox::new(false)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn box_id(&self) -> u64 {
self.id
}
}
impl Display for RandomBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RandomBox()")
}
}