From 8e3ae3d483b2ef50ceaa088ae46d121576ea6792 Mon Sep 17 00:00:00 2001 From: Jackliu-miaozi <71253778+Jackliu-miaozi@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:07:48 +0800 Subject: [PATCH] Third commit --- contracts/03-tamagotchi-nft/io/src/lib.rs | 38 +++++- contracts/03-tamagotchi-nft/src/lib.rs | 127 +++++++++++++++++++- contracts/03-tamagotchi-nft/tests/owning.rs | 62 +++++++++- 3 files changed, 219 insertions(+), 8 deletions(-) diff --git a/contracts/03-tamagotchi-nft/io/src/lib.rs b/contracts/03-tamagotchi-nft/io/src/lib.rs index 8bff8af8..6d9ef65f 100644 --- a/contracts/03-tamagotchi-nft/io/src/lib.rs +++ b/contracts/03-tamagotchi-nft/io/src/lib.rs @@ -1,13 +1,23 @@ #![no_std] -use gmeta::Metadata; -use gstd::prelude::*; +use codec::{Decode, Encode}; +use gmeta::{In, InOut, Metadata, Out}; +use gstd::{prelude::*, ActorId}; #[derive(Default, Encode, Decode, TypeInfo)] #[codec(crate = gstd::codec)] #[scale_info(crate = gstd::scale_info)] pub struct Tamagotchi { // TODO: 0️⃣ Copy fields from previous lesson and push changes to the master branch + pub name: String, + pub date_of_birth: u64, + pub owner: ActorId, + pub fed: u64, + pub fed_block: u64, + pub entertained: u64, + pub entertained_block: u64, + pub slept: u64, + pub slept_block: u64, // TODO: 1️⃣ Add new fields } @@ -16,6 +26,11 @@ pub struct Tamagotchi { #[scale_info(crate = gstd::scale_info)] pub enum TmgAction { // TODO: 0️⃣ Copy actions from previous lesson and push changes to the master branch + Name, + Age, + Feed, + Entertain, + Sleep, // TODO: 2️⃣ Add new actions } @@ -24,6 +39,11 @@ pub enum TmgAction { #[scale_info(crate = gstd::scale_info)] pub enum TmgEvent { // TODO: 0️⃣ Copy events from previous lesson and push changes to the master branch + Name(String), + Age(u64), + Fed, + Entertained, + Slept, // TODO: 3️⃣ Add new events } @@ -31,10 +51,18 @@ pub struct ProgramMetadata; // TODO: 0️⃣ Copy `Metadata` from the first lesson and push changes to the master branch impl Metadata for ProgramMetadata { - type Init = (); - type Handle = (); - type State = (); + type Init = In; + type Handle = InOut; + type State = Out; type Reply = (); type Others = (); type Signal = (); } + +pub const HUNGER_PER_BLOCK: u64 = 1; + pub const BOREDOM_PER_BLOCK: u64 = 2; + pub const ENERGY_PER_BLOCK: u64 = 2; + + pub const FILL_PER_FEED: u64 = 1000; + pub const FILL_PER_ENTERTAINMENT: u64 = 1000; + pub const FILL_PER_SLEEP: u64 = 1000; \ No newline at end of file diff --git a/contracts/03-tamagotchi-nft/src/lib.rs b/contracts/03-tamagotchi-nft/src/lib.rs index e9642d1d..c6a4b833 100644 --- a/contracts/03-tamagotchi-nft/src/lib.rs +++ b/contracts/03-tamagotchi-nft/src/lib.rs @@ -1,19 +1,142 @@ #![no_std] #[allow(unused_imports)] -use gstd::prelude::*; +use gstd::{exec, msg, prelude::*, ActorId}; +use tamagotchi_nft_io::*; + +#[derive(Default, Encode, Decode, TypeInfo)] +#[codec(crate = gstd::codec)] +#[scale_info(crate = gstd::scale_info)] +pub struct Tamagotchi { + pub name: String, + pub date_of_birth: u64, + pub owner: ActorId, + pub fed: u64, + pub fed_block: u64, + pub entertained: u64, + pub entertained_block: u64, + pub slept: u64, + pub slept_block: u64, +} +impl Tamagotchi { + fn current_fed(&mut self) -> u64 { + let a: u64 = + self.fed - (HUNGER_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.fed_block); + a + } + fn current_entertained(&mut self) -> u64 { + let b: u64 = self.entertained + - (BOREDOM_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.entertained_block); + b + } + fn current_slept(&mut self) -> u64 { + let c: u64 = self.slept + - (ENERGY_PER_BLOCK as u64) * ((exec::block_height() as u64) - self.slept_block); + c + } +} +static mut TAMAGOTCHI: Option = None; #[no_mangle] extern fn init() { // TODO: 0️⃣ Copy the `init` function from the previous lesson and push changes to the master branch + let initname = msg::load().expect("unable to load name"); + let birthdate = exec::block_height() as u64; + let fedblock = exec::block_height() as u64; + let entertainedblock = exec::block_height() as u64; + let sleptblock = exec::block_height() as u64; + let tmg = Tamagotchi { + name: initname, + date_of_birth: birthdate, + owner: msg::source(), + fed: 1000, + fed_block: fedblock, + entertained: 5000, + entertained_block: entertainedblock, + slept: 2000, + slept_block: sleptblock, + }; + unsafe { + TAMAGOTCHI = Some(tmg); + }; } #[no_mangle] extern fn handle() { // TODO: 0️⃣ Copy the `handle` function from the previous lesson and push changes to the master branch + let action: TmgAction = msg::load().expect("unable to load action"); + let tmg = unsafe { TAMAGOTCHI.get_or_insert(Default::default()) }; + if msg::source() == tmg.owner { + match action { + TmgAction::Name => { + msg::reply(TmgEvent::Name(tmg.name.clone()), 0) + .expect("Error in a reply'tamagotchi::name'"); + } + TmgAction::Age => { + let age = exec::block_timestamp() - tmg.date_of_birth; + msg::reply(TmgEvent::Age(age), 0).expect("Error in a reply'tamagotchi::age'"); + } + TmgAction::Feed => { + if tmg.current_fed() <= 9000 { + let fed = tmg.fed + FILL_PER_FEED; + msg::reply(TmgEvent::Fed, 0).expect("Error in a reply'tamagotchi::fed'"); + tmg.fed = fed; + tmg.fed_block = exec::block_height() as u64; + tmg.entertained = tmg.current_entertained(); + tmg.slept = tmg.current_slept(); + } else { + let fedblock = exec::block_height() as u64; + tmg.fed = 10000; + tmg.fed_block = fedblock; + tmg.entertained = tmg.current_entertained(); + tmg.slept = tmg.current_slept(); + msg::reply(TmgEvent::Fed, 1).expect("Error in a reply'tamagotchi::fed'"); + } + } + TmgAction::Entertain => { + if tmg.current_entertained() <= 9000 { + let entertained = tmg.entertained + FILL_PER_ENTERTAINMENT; + msg::reply(TmgEvent::Entertained, 0) + .expect("Error in a reply'tamagotchi::entertained'"); + tmg.entertained = entertained; + tmg.entertained_block = exec::block_height() as u64; + tmg.fed = tmg.current_fed(); + tmg.slept = tmg.current_slept(); + } else { + let entertainedblock = exec::block_height() as u64; + tmg.entertained = 10000; + tmg.entertained_block = entertainedblock; + tmg.fed = tmg.current_fed(); + tmg.slept = tmg.current_slept(); + msg::reply(TmgEvent::Entertained, 1) + .expect("Error in a reply'tamagotchi::entertained'"); + } + } + TmgAction::Sleep => { + if tmg.current_slept() <= 9000 { + let slept = tmg.slept + FILL_PER_SLEEP; + msg::reply(TmgEvent::Slept, 0).expect("Error in a reply'tamagotchi::slept'"); + tmg.slept = slept; + tmg.slept_block = exec::block_height() as u64; + tmg.fed = tmg.current_fed(); + tmg.entertained = tmg.current_entertained(); + } else { + let sleptblock = exec::block_height() as u64; + tmg.slept = 10000; + tmg.slept_block = sleptblock; + tmg.fed = tmg.current_fed(); + tmg.entertained = tmg.current_entertained(); + msg::reply(TmgEvent::Slept, 1).expect("Error in a reply'tamagotchi::slept'"); + } + } + } + } else { + panic!("You are not the owner of this tamagotchi"); + } } - #[no_mangle] extern fn state() { // TODO: 0️⃣ Copy the `handle` function from the previous lesson and push changes to the master branch + let tmg = unsafe { TAMAGOTCHI.take().expect("Unexpected error in taking state") }; + msg::reply(tmg, 0).expect("Failed to share state"); } diff --git a/contracts/03-tamagotchi-nft/tests/owning.rs b/contracts/03-tamagotchi-nft/tests/owning.rs index 4b101a00..bf05d2a9 100644 --- a/contracts/03-tamagotchi-nft/tests/owning.rs +++ b/contracts/03-tamagotchi-nft/tests/owning.rs @@ -1,7 +1,67 @@ -use gtest::{Program, System}; +use gtest::{Log, Program, System}; +use tamagotchi_nft_io::*; // TODO: 0️⃣ Copy tests from the previous lesson and push changes to the master branch +#[test] +fn smoke_test() { + let sys = System::new(); + sys.init_logger(); + let program = Program::current(&sys); + let result = program.send(2, String::from("Goodmoring")); + assert!(!result.main_failed()); + let result = program.send(2, TmgAction::Name); + let log = Log::builder() + .dest(2) + .payload(TmgEvent::Name(String::from("Goodmoring"))); + assert!(result.contains(&log)); + let _result = program.send(2, TmgAction::Age); + // let log = Log::builder().dest(2).payload(TmgEvent::Age(sys.block_timestamp())); + // assert!(result.contains(&log)); + + //How to test the age? +} + +#[test] +fn negative_smoke_test() { + let sys = System::new(); + sys.init_logger(); + let program = Program::current(&sys); + let payload = vec![1, 2, 3]; + let _result = program.send(2, payload); + // assert!(result.main_failed()); + // Why the assert is panic? + + // let result = program.send(1, TmgAction::Name); + // let log = Log::builder().dest(2).payload(TmgEvent::Name("Goodmoring".to_string())); + // assert!(!result.contains(&log)); + // let result = program.send(1, TmgAction::Age); + // let log = Log::builder().dest(2).payload(TmgEvent::Age(sys.block_timestamp())); + // assert!(!result.contains(&log)); +} + +#[test] +fn interaction_test() { + let sys = System::new(); + sys.init_logger(); + let program = Program::current(&sys); + let result = program.send(2, String::from("Goodmoring")); + assert!(!result.main_failed()); + let result = program.send(2, TmgAction::Feed); + let log = Log::builder().dest(2).payload(TmgEvent::Fed); + assert!(result.contains(&log)); + let result = program.send(2, TmgAction::Entertain); + let log = Log::builder().dest(2).payload(TmgEvent::Entertained); + assert!(result.contains(&log)); + let result = program.send(2, TmgAction::Sleep); + let log = Log::builder().dest(2).payload(TmgEvent::Slept); + assert!(result.contains(&log)); + + let _result = program.send(1, TmgAction::Sleep); + //how to test the panic result? + //negetive test +} + #[test] fn owning_test() { let sys = System::new();