サッカーゲームをRustで書く

サッカーゲームというより、単なるPK戦だなぁ〜
もう少しリアリティのあるゲームにしたい…

use std::io;
use rand::Rng;

struct Game {
   player_score: i32,
   cpu_score: i32,
}

impl Game {

   fn new() -> Self {
      Game {
         player_score: 0,
         cpu_score: 0,
      }
   }

   fn player_attack() -> i32 {
      println!("攻撃! シュートする方向を選んでください:");
      println!("1: 左\n2: 中央\n3: 右");
      let mut input = String::new();
      io::stdin()
         .read_line(&mut input)
         .expect("入力の読み取りに失敗しました");
      let dir: i32 = match input.trim().parse() {
         Ok(num) => num,
         Err(_) => {
            println!("無効な入力です。数字を入力してください。");
            return 0;
         }
      };
      return dir;
   }

   fn cpu_defend() -> i32 {
      let mut rng = rand::thread_rng();
      return rng.gen_range(1..=3);
   }

   fn cpu_attack() -> i32 {
      let mut rng = rand::thread_rng();
      return rng.gen_range(1..=3);
   }

   fn player_defend() -> i32 {
      println!("守備!相手のシュートを読む方向を選んでください:");
      println!("1: 左\n2: 中央\n3: 右");
      let mut input = String::new();
      io::stdin()
         .read_line(&mut input)
         .expect("入力の読み取りに失敗しました");
      let dir: i32 = match input.trim().parse() {
         Ok(num) => num,
         Err(_) => {
            println!("無効な入力です。数字を入力してください。");
            return 0;
         }
      };
      return dir;
   }
}

fn main() {
   let mut game1 = Game::new();
   println!("{}", game1.player_score);

   for turn in 1..4 {
      println!("--- 第{}ターン ---", turn);
      let player_dir = Game::player_attack();
      let cpu_dir = Game::cpu_defend();

      if player_dir != cpu_dir {
         println!("ゴール!! 🎉");
         game1.player_score += 1;
      } else {
         println!("セーブされました...🧤");
      }

      let cpu_dir = Game::cpu_attack();
      let player_dir = Game::player_defend();

      if player_dir != cpu_dir {
         println!("ゴール!! 😱");
         game1.cpu_score += 1;
      } else {
         println!("ナイスセーブ!! 🧤");
      }
   }

   println!("=== 試合終了 ===");

   println!("あなた: {}, CPU: {}", game1.player_score, game1.cpu_score);
}

— 第1ターン —
攻撃! シュートする方向を選んでください:
1: 左
2: 中央
3: 右
1
ゴール!! 🎉
守備!相手のシュートを読む方向を選んでください:
1: 左
2: 中央
3: 右
2
ゴール!! 😱
— 第2ターン —
攻撃! シュートする方向を選んでください:
1: 左
2: 中央
3: 右
1
ゴール!! 🎉
守備!相手のシュートを読む方向を選んでください:
1: 左
2: 中央
3: 右
1
ゴール!! 😱
— 第3ターン —
攻撃! シュートする方向を選んでください:
1: 左
2: 中央
3: 右
1
セーブされました…🧤
守備!相手のシュートを読む方向を選んでください:
1: 左
2: 中央
3: 右
1
ゴール!! 😱
=== 試合終了 ===
あなた: 2, CPU: 3

【Rust】フレームワーク自作: Postメソッドを実装する

getとpostを分ける

use std::fs;
use std::io;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::collections::HashMap;
use crate::main;

pub struct Router {
    get_routes: HashMap<String, String>,
    post_routes: HashMap<String, fn(String) -> String>,
}

impl Router {

    pub fn new() -> Self {
        Router {
            get_routes: HashMap::new(),
            post_routes: HashMap::new(),
        }
    }

    pub fn add_get(&mut self, path: &str, f: fn()-> Option<HashMap<&'static str, &'static str>>){
        let mut content: Option<HashMap<&str, &str>> = Some(HashMap::new());
        content = f();

        let temp_path = format!("./templates/{}.html", path);
        let mut html = fs::read_to_string(temp_path).unwrap();

        match content {
            Some(data) => {
                for (key, value) in data {
                    let k = format!("{{{{ {} }}}}", key);
                    html = html.replace(&k, value);
                }
            },
            None => {},
        }
        let route_path = format!("/{}", path);
        self.get_routes.insert(route_path, html);
    }

    pub fn add_post(&mut self, path: &str, handler: fn(String) -> String) {
        self.post_routes.insert(format!("/{}", path), handler);
    }

    pub fn up(&self, ip: &str) {
        let listenr = TcpListener::bind(ip).unwrap();
        for stream in listenr.incoming() {
            match stream {
                Ok(stream) => {
                    let _ = handle_connection(
                        stream, 
                        self.get_routes.clone(),
                        self.post_routes.clone(),
                    );
                }
                Err(e) => {
                    println!("Connection failed: {}", e);
                }
            }
        }
    }
}

fn handle_connection(mut stream: TcpStream, get_routes: HashMap<String, String>, post_routes: HashMap<String, fn(String) -> String>)  {
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();
 
    let request = String::from_utf8_lossy(&buffer);
    let request_line = request.lines().next().unwrap_or("");
    let mut parts = request_line.split_whitespace();
    let method = parts.next().unwrap_or("");
    let path = parts.next().unwrap_or("/");

    println!("Received {} request for {}", method, path);
 

    let response_body = match method {
        "GET" => get_routes.get(path).cloned().unwrap_or_else(|| "<h1>404 Not Found</h1>".to_string()),
        "POST" => {
            let body = request.split("\r\n\r\n").nth(1).unwrap_or("").to_string();
            match post_routes.get(path) {
                Some(handler) => handler(body),
                None => "<h1>404 Not Found</h1>".to_string(),
            }
        }
        _ => "<h1>405 Method Not Allowed</h1>".to_string(),
    };
 
    let response = http_response(200, "text/html", &response_body);
 
    stream.write_all(response.as_bytes()).unwrap();
    stream.flush().unwrap();
 }
 
 fn http_response(status_code: u16, content_type: &str, body: &str) -> String {
    format!(
        "HTTP/1.1 {} {}\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n{}",
        status_code,
        get_status_text(status_code),
        content_type,
        body.len(),
        body
    )
 }
 
 fn get_status_text(code: u16) -> &'static str {
    match code {
        200 => "OK",
        404 => "Not Found",
        _ => "Unknown",
    }
 }

### 利用側

use std::collections::HashMap;
mod ares;

fn main() {
    let mut router = ares::Router::new();

    router.add_get("index", handle_index);
    router.add_get("hello", handle_hello);
    router.add_get("world", handle_world);
    router.add_post("submit", handle_post);

    router.up("192.168.33.10:8000");
}


fn handle_index() -> Option<HashMap<&'static str, &'static str>> {

    let mut content1 = HashMap::new();
    content1.insert(
        "title",
        "index page",
    );
    return Some(content1);
}

fn handle_hello() -> Option<HashMap<&'static str, &'static str>> {

    return None;
}

fn handle_world() -> Option<HashMap<&'static str, &'static str>> {

    return None;
}

fn handle_post(body: String) -> String {
    println!("{}", body);
    format!("<h1>POST 受信しました</h1><p>内容: {}</p>", body)
}

ほう…

【Rust】関数名を引数にして関数を呼び出したい

fn()とすることで、引数を関数にすることができる。

fn main() {
    run_function(handle_index);
}

fn run_function(f: fn()-> Option<HashMap<&'static str, &'static str>>) {
    let mut content: Option<HashMap<&str, &str>> = Some(HashMap::new());
    content = f();
    match content {
        Some(data) => {
            println!("{:?}", data);
        },
        None => {},
    }
}

fn handle_index() -> Option<HashMap<&'static str, &'static str>> {

    let mut content1 = HashMap::new();
    content1.insert(
        "title",
        "index page",
    );
    return Some(content1);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.45s
Running `target/debug/ares`
{“title”: “index page”}

なるほどー、中々やるやんけ

Rustで簡単なフレームワークを自作したい5

main.rs

use std::collections::HashMap;
mod ares;

fn main() {
    
    let mut content = HashMap::new();
    content.insert(
        "title",
        "index page",
    );
    content.insert(
        "data1",
        "10000",
    );

    let _ = ares::Router::new("index", Some(content));
    
}

### フレームワーク側
ares.rs

use std::fs;
use std::io;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::collections::HashMap;

pub struct Router;

impl Router {
    pub fn new(path: &str, content: Option<HashMap<&str, &str>>) {

        let mut routes = HashMap::new();
        let temp_path = format!("./templates/{}.html", path);
        let mut html = fs::read_to_string(temp_path).unwrap();

        match content {
            Some(data) => {
                for (key, value) in data {
                    let k = format!("{{{{ {} }}}}", key);
                    html = html.replace(&k, value);
                }
            },
            None => {},
        }
        let route_path = format!("/{}", path);
        routes.insert(
            route_path, html,
        );
        println!("{:?}", routes);


        let listenr = TcpListener::bind("192.168.33.10:8000").unwrap();
        for stream in listenr.incoming() {
            match stream {
                Ok(stream) => {
                    let _ = handle_connection(stream, routes.clone());
                }
                Err(e) => {
                    println!("Connection failed: {}", e);
                }
            }
        }
    }
}

fn handle_connection(mut stream: TcpStream, routes: HashMap<String, String>)  {
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();
 
    let request = String::from_utf8_lossy(&buffer);
    let request_line = request.lines().next().unwrap_or("");
    println!("Received request:\n{}", request);
 
    let path = request_line
         .split_whitespace()
         .nth(1)
         .unwrap_or("/");
    
    let response_body = routes.get(path)
         .cloned()
         .unwrap_or_else(|| "<h1>404 Not Found</h1>".to_string());
 
    let response = http_response(200, "text/html", &response_body);
 
    stream.write_all(response.as_bytes()).unwrap();
    stream.flush().unwrap();
 }
 
 fn http_response(status_code: u16, content_type: &str, body: &str) -> String {
    format!(
        "HTTP/1.1 {} {}\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n{}",
        status_code,
        get_status_text(status_code),
        content_type,
        body.len(),
        body
    )
 }
 
 fn get_status_text(code: u16) -> &'static str {
    match code {
        200 => "OK",
        404 => "Not Found",
        _ => "Unknown",
    }
 }

複数パスに対応できるようにしたい。

### 複数パスに対応
hashmapにパスとコンテンツを追加するように実装する

use std::collections::HashMap;
mod ares;

fn main() {
    let mut router = ares::Router::new();

    let mut content1 = HashMap::new();
    content1.insert(
        "title",
        "index page",
    );

    let mut content2 = HashMap::new();
    content2.insert(
        "title",
        "this is title",
    );

    router.add("index", Some(content1));
    router.add("hello", None);
    router.add("world", Some(content2));

    router.up("192.168.33.10:8000");   
}
pub struct Router {
    route: HashMap<String, String>,
}

impl Router {

    pub fn new() -> Self {
        Router {
            route: HashMap::new(),
        }
    }

    pub fn add(&mut self, path: &str, content: Option<HashMap<&str, &str>>){
        let temp_path = format!("./templates/{}.html", path);
        let mut html = fs::read_to_string(temp_path).unwrap();

        match content {
            Some(data) => {
                for (key, value) in data {
                    let k = format!("{{{{ {} }}}}", key);
                    html = html.replace(&k, value);
                }
            },
            None => {},
        }
        let route_path = format!("/{}", path);
        self.route.insert(route_path, html);
    }

    pub fn up(&self, ip: &str) {
        let listenr = TcpListener::bind(ip).unwrap();
        for stream in listenr.incoming() {
            match stream {
                Ok(stream) => {
                    let _ = handle_connection(stream, self.route.clone());
                }
                Err(e) => {
                    println!("Connection failed: {}", e);
                }
            }
        }
    }
}

MVCモデルにしたい…

フレームワークを自作したい4

templatesというディレクトリに、ルート名と同じファイル名でhtmlファイルを作成するというルールにする。

fn main() {

   let address = "192.168.33.10:8000";

   let mut routes = HashMap::new();

   let mut paths: Vec<&str> = Vec::new();
   paths.push("index");
   paths.push("hello");

   for path in paths {
        let temp_path = format!("./templates/{}.html", path);
        let content = fs::read_to_string(temp_path).unwrap();

        let route_path = format!("/{}", path);
        routes.insert(
            route_path, content,
        );
   }
   println!("{:?}", routes);
}

{“/index”: “

Hello from Rust!

“, “/hello”: “

Welcome to the Rust server!

“}

teraの場合は、tera.renderとして、任意のファイル名を指定できるようにしている。これも、hashmapで、ルート名とファイル名をkey, valueで持っていれば、問題なくできそうでる。

### テンプレートエンジンの変数の扱い
hashmapでkeyとvalueを格納して、htmlの{{ @name }} とkeyが一致したものをvalueに変換すればできそうである。。

let mut context = tera::Context::new();
context.insert("title", "Index page");
{{title}}

### Rustで{{ }}を置換するように実装

    let mut content = HashMap::new();
    content.insert(
        "title",
        "index page",
    );
    let mut data = HashMap::new();
    for (key, value) in content {
        let k = format!("{{{{ {} }}}}", key);
        data.insert(
            k, value,
        );
    };
    println!("{:?}", data);

    let mut str = "<h1>Welcome to the {{ title }} server!</h1>";

    for (key, value) in data {
        let s = &str.replace(&key, value);
        println!("{}", s);
    };

### テンプレートに引き渡す変数がある時
pathにhashmapで変数のセットを紐づけて、後で置換するようにする。

let mut content = HashMap::new();
    content.insert(
        "title",
        "index page",
    );
    let mut data = HashMap::new();
    for (key, value) in content {
        let k = format!("{{{{ {} }}}}", key);
        data.insert(
            k, value,
        );
    };

    let mut str = "<h1>Welcome to the {{ title }} server!</h1>";
    
    let mut paths: Vec<(&str, Option<HashMap<String, &str>>)> = Vec::new();
    paths.push(("index", Some(data)));
    paths.push(("hello", None));

    println!("{:?}", paths);

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/ares`
[(“index”, Some({“{{ title }}”: “index page”})), (“hello”, None)]

### 全部繋げる

let mut paths: Vec<(&str, Option<HashMap<String, &str>>)> = Vec::new();

    let mut content = HashMap::new();
    content.insert(
        "title",
        "index page",
    );
    content.insert(
        "data1",
        "10000",
    );
    let mut data = HashMap::new();
    for (key, value) in content {
        let k = format!("{{{{ {} }}}}", key);
        data.insert(
            k, value,
        );
    };

    paths.push(("index", Some(data)));
    paths.push(("hello", None));

    let mut routes = HashMap::new();

    for (path, v) in paths {
        let temp_path = format!("./templates/{}.html", path);
        let mut content = fs::read_to_string(temp_path).unwrap();
        match v {
            Some(data) => 
                for (key, value) in data {
                    content = content.replace(&key, value);
                },
            None => {},
        }
        let route_path = format!("/{}", path);
        routes.insert(
            route_path, content,
        );
    };
    println!("{:?}", routes);

{“/index”: “

Hello from Rust! title:index page

“, “/hello”: “

Welcome to the Rust server!

“}

三角形の周長

fn main() {

   let mut ans: i32 = 0;
   let n: i32 = 10;

   for i in 0..n {
      let j = i + 1;
      for j in j..n {
         let k = j + 1;
         for k in k..n {
            let len = i + j + k;
            let ma = std::cmp::max(i, std::cmp::max(j, k));
            let rest = len - ma;

            if (ma < rest) {
               ans = std::cmp::max(ans, len);
            }
         }
      }
   }
   println!("{}", ans);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
Running `target/debug/basic`
24

【Rust】プロセススケジュラー

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ProcessState {
    Ready,
    Running,
    Waiting,
    Terminated,
}

#[derive(Debug)]
struct Process {
    pid: usize,
    state: ProcessState,
}

struct Scheduler {
    processes: Vec<Process>,
    current_index: usize,
}

impl Scheduler {
    fn new(processes: Vec<Process>) -> Self {
        Scheduler {
            processes,
            current_index: 0,
        }
    }

    fn schedule(&mut self) -> Option<&Process> {
        if self.processes.is_empty() {
            return None;
        }

        let len = self.processes.len();
        for _ in 0..len {
            self.current_index = (self.current_index + 1) % len;
            let proc = &mut self.processes[self.current_index];
            if proc.state == ProcessState::Ready {
                proc.state = ProcessState::Running;
                return Some(proc);
            }
        }
        None
    }

    fn update_state(&mut self, pid: usize, new_state: ProcessState) {
        if let Some(proc) = self.processes.iter_mut().find(|p| p.pid == pid) {
            proc.state = new_state;
        }
    }
}


fn main(){
    
    let mut scheduler = Scheduler::new(vec![
        Process { pid: 1, state: ProcessState::Ready},
        Process { pid: 2, state: ProcessState::Ready},
        Process { pid: 3, state: ProcessState::Waiting},
    ]);

    for _ in 0..5 {
        if let Some(proc) = scheduler.schedule() {
            println!("Running process: {:?}", proc.pid);
            scheduler.update_state(proc.pid, ProcessState::Ready);
        } else {
            println!("No Runnable processes.");
            break;
        }
    }
}

【Rust】メモリ管理

const FRAME_SIZE: usize = 4096;
const TOTAL_MEMORY: usize = 1024 * 1024 * 128;
const TOTAL_FRAMES: usize = TOTAL_MEMORY / FRAME_SIZE;

static mut FRAME_BITMAP: [bool; TOTAL_FRAMES] = [false; TOTAL_FRAMES];

pub struct FrameAllocator;

impl FrameAllocator {
    pub fn alloc() -> Option<usize> {
        unsafe {
            for (i, used) in FRAME_BITMAP.iter_mut() enumerate() {
                if !*used {
                    *used = true;
                    return Some(i * FRAME_SIZE);
                }
            }
            None
        }
    }

    pub fn dealloc(addr: usize) {
        let index = addr / FRAME_SIZE;
        if index < TOTAL_FRAMES {
            unsafe {
                FRAME_BITMAP[index] = false;
            }
        }
    }
}

【Rust】バイナリデータへの処理

### 文字列の変換

fn main() {
    let text = "Hello, Rust!";
    let binary = text.as_bytes();

    println!("binary: {:?}", binary);
}

binary: [72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]

### テキストファイルの変換
input.txt

Hello Rust!
This is sample
use std::fs::File;
use std::io::{self, Read, Write};

fn main() -> io::Result<()>{
    let mut input_file = File::open("./data/input.txt")?;

    let mut buffer = Vec::new();
    input_file.read_to_end(&mut buffer)?;

    println!("binary: {:?}", &buffer);

    Ok(())
}

### データのchunk

fn main(){
    
    let data = vec![0u8; 10000];

    let chunk_size = 1024;

    for(i, chunk) in data.chunks(chunk_size).enumerate() {
        println!("チャンク {}: サイズ = {}", i, chunk.len());
    }
}

チャンク 0: サイズ = 1024
チャンク 1: サイズ = 1024
チャンク 2: サイズ = 1024
チャンク 3: サイズ = 1024
チャンク 4: サイズ = 1024
チャンク 5: サイズ = 1024
チャンク 6: サイズ = 1024
チャンク 7: サイズ = 1024
チャンク 8: サイズ = 1024
チャンク 9: サイズ = 784

### 画像データのchunk

use std::fs::File;
use std::io::{self, Read, Write};

fn main()-> io::Result<()>{
    
    let mut input_file = File::open("./data/test.jpg")?;

    let mut buffer = Vec::new();
    input_file.read_to_end(&mut buffer)?;

    let chunk_size = 1024;

    for(i, chunk) in buffer.chunks(chunk_size).enumerate() {
        println!("チャンク {}: 値 = {:?}", i, chunk);
    }

    Ok(())
}

Rustでmalloc, free

use std::alloc::{alloc, dealloc, Layout};
use std::ptr;

fn main() {
    unsafe {
        let layout = Layout::new::<i32>();
        let ptr = alloc(layout) as *mut i32;

        if ptr.is_null() {
            panic!("メモリ確保に失敗しました");
        }

        *ptr = 42;
        println!("ptrが指す値: {}", *ptr);

        dealloc(ptr as *mut u8, layout);
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.83s
Running `target/debug/memory`
ptrが指す値: 42