Last active
July 6, 2019 21:45
-
-
Save loloof64/85a794d120cffe347535ad147cc4a7b8 to your computer and use it in GitHub Desktop.
TicTacToe in Rust+Yew (Launching with cargo web)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "tic-tac-toe-asm" | |
version = "0.1.0" | |
authors = ["Laurent Bernabé <[email protected]>"] | |
edition = "2018" | |
[dependencies] | |
yew = "0.6.0" | |
stdweb = "0.4.17" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![recursion_limit = "128"] | |
use std::f64::consts::PI; | |
use yew::{html, Component, ComponentLink, Html, Renderable, ShouldRender}; | |
use stdweb::web::{document, IParentNode, CanvasRenderingContext2d, FillRule}; | |
use stdweb::web::html_element::CanvasElement; | |
use stdweb::unstable::TryInto; | |
use stdweb::web::event::{ClickEvent, IMouseEvent}; | |
const CELLS_SIZE : i32 = 100; | |
#[derive(Copy, Clone)] | |
pub enum Player { | |
Circle, | |
Cross, | |
None, | |
} | |
pub struct Model { | |
current_player: Player, | |
grid: [[Player; 3]; 3], | |
} | |
pub enum Msg { | |
Play(ClickEvent), | |
Reset, | |
} | |
impl Model { | |
fn get_canvas_context(&self) -> CanvasRenderingContext2d { | |
let canvas: CanvasElement = | |
document() | |
.query_selector("#render_zone") | |
.expect("Failed to get render zone !") | |
.expect("Failed to get render zone !") | |
.try_into() | |
.expect("Failed to get render zone !"); | |
let context: CanvasRenderingContext2d = | |
canvas.get_context().expect("Failed to get render zone context !"); | |
context | |
} | |
fn draw_piece(&self, piece_type: Player, rank: i8, file: i8) | |
{ | |
match piece_type { | |
Player::Cross => self.draw_cross(rank, file), | |
Player::Circle => self.draw_circle(rank, file), | |
_ => {}, | |
}; | |
} | |
fn draw_circle(&self, rank: i8, file: i8) | |
{ | |
let context = self.get_canvas_context(); | |
context.set_fill_style_color("#ff0000"); | |
context.begin_path(); | |
context.arc( | |
(CELLS_SIZE as f64) * ((file as f64) + 0.5), | |
(CELLS_SIZE as f64) * ((rank as f64) + 0.5), | |
(CELLS_SIZE as f64) * 0.4, | |
0.0, | |
2.0*PI, | |
false, | |
); | |
context.fill(FillRule::NonZero); | |
context.set_fill_style_color("#ffffff"); | |
context.begin_path(); | |
context.arc( | |
(CELLS_SIZE as f64) * ((file as f64) + 0.5), | |
(CELLS_SIZE as f64) * ((rank as f64) + 0.5), | |
(CELLS_SIZE as f64) * 0.3, | |
0.0, | |
2.0*PI, | |
false, | |
); | |
context.fill(FillRule::NonZero); | |
} | |
fn draw_cross(&self, rank: i8, file: i8) | |
{ | |
let context = self.get_canvas_context(); | |
context.save(); | |
context.set_line_width(3.0); | |
context.set_fill_style_color("#0000ff"); | |
context.begin_path(); | |
context.move_to( | |
(CELLS_SIZE as f64) * ((file as f64) + 0.1), | |
(CELLS_SIZE as f64) * ((rank as f64) + 0.1), | |
); | |
context.line_to( | |
(CELLS_SIZE as f64) * ((file as f64) + 0.9), | |
(CELLS_SIZE as f64) * ((rank as f64) + 0.9), | |
); | |
context.stroke(); | |
context.begin_path(); | |
context.move_to( | |
(CELLS_SIZE as f64) * ((file as f64) + 0.1), | |
(CELLS_SIZE as f64) * ((rank as f64) + 0.9), | |
); | |
context.line_to( | |
(CELLS_SIZE as f64) * ((file as f64) + 0.9), | |
(CELLS_SIZE as f64) * ((rank as f64) + 0.1), | |
); | |
context.stroke(); | |
context.restore(); | |
} | |
fn reset_zone(&self) | |
{ | |
let context = self.get_canvas_context(); | |
context.clear_rect(0.0, 0.0, (CELLS_SIZE as f64) * 3.0, (CELLS_SIZE as f64) * 3.0); | |
context.set_fill_style_color("#000"); | |
for col in 1..3 { | |
context.begin_path(); | |
context.move_to((CELLS_SIZE as f64) * (col as f64), 0.0); | |
context.line_to((CELLS_SIZE as f64) * (col as f64), (CELLS_SIZE as f64) * 3.0); | |
context.stroke(); | |
} | |
for row in 1..3 { | |
context.begin_path(); | |
context.move_to(0.0, (CELLS_SIZE as f64) * (row as f64)); | |
context.line_to((CELLS_SIZE as f64) * 3.0, (CELLS_SIZE as f64) * (row as f64)); | |
context.stroke(); | |
} | |
} | |
} | |
impl Component for Model { | |
type Message = Msg; | |
type Properties = (); | |
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self { | |
Model { | |
current_player: Player::None, | |
grid: [[Player::None; 3]; 3], | |
} | |
} | |
fn update(&mut self, msg: Self::Message) -> ShouldRender { | |
match msg { | |
Msg::Play(event) => { | |
let file = ((event.client_x() as f64) / (CELLS_SIZE as f64)) as i8; | |
let rank = ((event.client_y() as f64) / (CELLS_SIZE as f64)) as i8; | |
if file < 0 || file > 2 { return false; }; | |
if rank < 0 || rank > 2 { return false; }; | |
let cell_empty = match self.grid[rank as usize][file as usize]{ | |
Player::None => true, | |
_ => false, | |
}; | |
if cell_empty | |
{ | |
self.grid[rank as usize][file as usize] = self.current_player; | |
self.draw_piece(self.current_player, rank, file); | |
self.current_player = match self.current_player { | |
Player::Circle => Player::Cross, | |
Player::Cross => Player::Circle, | |
Player::None => Player::None, | |
} | |
}; | |
return true; | |
}, | |
Msg::Reset => { | |
for rank in 0..3 { | |
for file in 0..3 { | |
self.grid[rank][file] = Player::None; | |
} | |
} | |
self.reset_zone(); | |
self.current_player = Player::Cross; | |
return true; | |
} | |
} | |
} | |
} | |
impl Renderable<Model> for Model { | |
fn view(&self) -> Html<Self> { | |
html! { | |
<> | |
<button | |
onclick=|_event| Msg::Reset, | |
> | |
{"Nouvelle partie"} | |
</button> | |
<div> | |
<canvas | |
id="render_zone", | |
width={format!("{}", CELLS_SIZE * 3)}, | |
height={format!("{}", CELLS_SIZE * 3)}, | |
onclick=|event| Msg::Play(event), | |
/> | |
</div> | |
<h3> | |
{format!("Joueur: {}", | |
match self.current_player { | |
Player::Circle => "O", | |
Player::Cross => "X", | |
_ => "", | |
} | |
)} | |
</h3> | |
</> | |
} | |
} | |
} | |
fn main() { | |
yew::start_app::<Model>(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment