Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newgame #41

Merged
merged 15 commits into from
Jun 24, 2021
Prev Previous commit
Next Next commit
added remove capability, needed to update other games to be consisten…
…t with tool
  • Loading branch information
Emma Tosch authored and Emma Tosch committed May 6, 2021
commit 3cc041e5995567d7166dda063771d9f6789621b1
2 changes: 1 addition & 1 deletion Games.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
games = ["Amidar", "Breakout", "GridWorld", "SpaceInvaders", "PongConfig"]
games = ["Amidar", "Breakout", "GridWorld", "SpaceInvaders", "Pong"]
191 changes: 167 additions & 24 deletions newgame/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::fs::{create_dir_all, read_to_string};
use std::io::BufReader;
/**
Steps to creating a new game.
- get a name, make sure it doesn't clash
Expand All @@ -10,6 +10,7 @@ use std::fs::{create_dir_all, read_to_string};
- re-generate toybox/src/lib.rs
*/
use std::{error::Error, io::Write};
use std::{fs::File, io::BufRead};
use subprocess::{Popen, PopenConfig, Redirection};
use toml::Value;

Expand Down Expand Up @@ -73,26 +74,32 @@ fn add_to_games(mainclass: String) -> Result<(), String> {
}
}

fn remove_from_games(game: String) -> Result<(), String> {
fn remove_from_games(mainclass: String) -> Result<(), String> {
// Get the existing game list
let path = "Games.toml";
let mut games: Games = toml::from_str(&read_to_string(path.clone()).unwrap()).unwrap();
// Remove from game list
let () = {
for i in 0..games.games.len() {
if games.games[i].to_lowercase().eq(&game.to_lowercase()) {
games.games.remove(i);
return Ok(());
}
let i = games.games.iter().position(|g| *g == mainclass);
let i = match i {
Some(i) => i,
None => {
println!(
"Either game {} not found in Games.toml, or already removed.",
&mainclass
);
return Ok(());
}
Err(format!("Game {} not found in Games.toml", &game))
}?;
};
games.games.remove(i);
// write new file
let s = toml::to_string(&games).unwrap();
let mut f: File = File::create(path.clone()).unwrap();
match f.write_all(s.as_bytes()) {
Err(msg) => Err(msg.to_string()),
_ => Ok(()),
_ => {
println!("Removed {} from Games.toml", &mainclass);
Ok(())
}
}
}

Expand All @@ -108,6 +115,30 @@ fn add_to_workspace(dir: String) -> Result<(), String> {
}
}

fn remove_from_workspace(dir: String) -> Result<(), String> {
// Get the existing Cargo.toml
let mut cargo_toml: TopCargo = toml::from_str(&read_to_string("Cargo.toml").unwrap()).unwrap();
let index = cargo_toml.workspace.members.iter().position(|d| *d == dir);
let index = match index {
Some(i) => i,
None => {
println!("Already removed {} from top level Cargo.toml", dir);
return Ok(());
}
};

cargo_toml.workspace.members.remove(index);
let s = toml::to_string(&cargo_toml).unwrap();
let mut f = File::create("Cargo.toml").unwrap();
match f.write_all(s.as_bytes()) {
Err(msg) => Err(msg.to_string()),
_ => {
println!("Removed {} from top level Cargo.toml", dir);
Ok(())
}
}
}

fn add_to_dependences(cargo_toml: &mut TBCargo, dir: &String, game: &String) -> Result<(), String> {
let deps = cargo_toml.dependencies.clone();
let new_deps = {
Expand All @@ -128,6 +159,34 @@ fn add_to_dependences(cargo_toml: &mut TBCargo, dir: &String, game: &String) ->
Ok(())
}

fn remove_from_dependences(
cargo_toml: &mut TBCargo,
dir: &String,
game: &String,
) -> Result<(), String> {
let deps = cargo_toml.dependencies.clone();
let new_deps = {
match deps {
Value::Table(mut m) => {
if m.contains_key(game.into()) {
m.remove(game.into());
Value::Table(m)
} else {
println!(
"Already removed dependencies.{} from toybox/Cargo.toml",
&game
);
return Ok(());
}
}
_ => return Err(cargo_toml.dependencies.to_string()),
}
};
cargo_toml.dependencies = new_deps;
println!("Removed dependencies.{} from toybox/Cargo.toml", &game);
Ok(())
}

fn add_to_features(cargo_toml: &mut TBCargo, game: &String) -> Result<(), String> {
let features = cargo_toml.features.clone();
let new_features = {
Expand All @@ -143,6 +202,33 @@ fn add_to_features(cargo_toml: &mut TBCargo, game: &String) -> Result<(), String
Ok(())
}

fn remove_from_features(cargo_toml: &mut TBCargo, game: &String) -> Result<(), String> {
let features = cargo_toml.features.clone();
let new_features = {
match features {
Some(mut f) => {
let index = f.default.iter().position(|g| g == game);
let index = match index {
Some(i) => i,
None => {
println!("Already removed feature {} from toybox/Cargo.toml", &game);
return Ok(());
}
};
f.default.remove(index);
Ok(Some(f))
}
None => Err("Input does not have a featurse attribute".to_string()),
}
}?;
cargo_toml.features = new_features;
println!(
"Removed feature dependencies.{} from toybox/Cargo.toml",
&game
);
Ok(())
}

fn add_to_toybox_cargo(dir: String, game: String) -> Result<(), String> {
// Can't make a struct because the dependencies table entry requires that
// we know the key names a priori. We need to add the new game to the
Expand All @@ -160,6 +246,20 @@ fn add_to_toybox_cargo(dir: String, game: String) -> Result<(), String> {
}
}

fn remove_from_toybox_cargo(dir: String, game: String) -> Result<(), String> {
// Load it up
let path = ["toybox", "Cargo.toml"].join(&std::path::MAIN_SEPARATOR.to_string());
let mut cargo_toml: TBCargo = toml::from_str(&read_to_string(path.clone()).unwrap()).unwrap();
remove_from_dependences(&mut cargo_toml, &dir, &game)?;
remove_from_features(&mut cargo_toml, &game)?;
let s = toml::to_string(&cargo_toml).unwrap();
let mut f = File::create(&path).unwrap();
match f.write_all(s.as_bytes()) {
Err(msg) => Err(msg.to_string()),
_ => Ok(()),
}
}

fn create_project_files(game: String, dir: String) -> Result<(), std::io::Error> {
let mut p = Popen::create(
&["cargo", "new", &*dir, "--lib"],
Expand Down Expand Up @@ -188,6 +288,22 @@ fn create_project_files(game: String, dir: String) -> Result<(), std::io::Error>
}
}

fn delete_project_files(dir: String) -> Result<(), String> {
let mut p = Popen::create(
&["rm", "-rf", &*dir],
PopenConfig {
stdout: Redirection::Pipe,
..Default::default()
},
)
.unwrap();
loop {
if p.poll().is_some() {
return Ok(());
}
}
}

fn update_newgame_cargo(dir: String, game: String) -> Result<(), String> {
// First we need to change the package name
let path = [dir, "Cargo.toml".into()].join(&std::path::MAIN_SEPARATOR.to_string());
Expand Down Expand Up @@ -242,8 +358,11 @@ fn update_toybox_lib(_dir: String, _gamename: String, _classname: String) -> Res

for old_class in &game_data.games {
let old_game = old_class.to_lowercase();
gamelist1.push(format!("#[cfg(feature = \"{game}\")]\n\"{game}\" => Ok(Box::new({game}::{class}::default())),\n",
game=old_game, class=old_class));
gamelist1.push(format!(
"#[cfg(feature = \"{game}\")]\n\"{game}\" => Ok(Box::new({game}::{class}::default())),",
game = old_game,
class = old_class
));
gamelist2.push(format!(
"#[cfg(feature = \"{game}\")]\n\"{game}\",\n",
game = old_game
Expand Down Expand Up @@ -286,22 +405,42 @@ fn populate_files(dir: String, game: String, mainclass: String) -> Result<(), St
Ok(())
}

fn remove_from_librs(mainclass: String) -> Result<(), String> {
let path = ["toybox", "src", "lib.rs"].join(&std::path::MAIN_SEPARATOR.to_string());
let game = mainclass.to_lowercase();
let f = File::open(&path).unwrap();
let mut s = String::new();
let reader = BufReader::new(f);
for line in reader.lines() {
let line = line.unwrap();
if !(line.contains(&game) || line.contains(&mainclass)) {
s.push_str(&line);
s.push_str("\n");
}
}
let mut f = File::create(&path).unwrap();
match &f.write_all(s.to_string().as_bytes()) {
Err(msg) => Err(msg.to_string()),
_ => Ok(()),
}
}

fn main() -> Result<(), Box<dyn Error>> {
// Do all I/O here
let mut verbose = false;
// let mut clear = false;
let mut clear = false;
let mut game_arg = String::new();
let () = {
let mut parser = ArgumentParser::new();
parser.set_description("Add a new rust game to Toybox.");
parser
.refer(&mut verbose)
.add_option(&["-v", "--verbose"], StoreTrue, "Be verbose");
// parser.refer(&mut clear).add_option(
// &["-c", "--clear"],
// StoreTrue,
// "Clears the game locally. WARNING: This deletes files!!",
// );
parser.refer(&mut clear).add_option(
&["-c", "--clear"],
StoreTrue,
"Clears the game locally. WARNING: This deletes files!!",
);
parser.refer(&mut game_arg).add_argument(
"new_game_name",
Store,
Expand All @@ -328,11 +467,15 @@ fn main() -> Result<(), Box<dyn Error>> {

// Let the processing begin!

// if clear {
// println!("Clearing game {}...", g.clone());
// remove_from_games(game.clone())?;
// return Ok(());
// }
if clear {
println!("Clearing game {}...", g.clone());
remove_from_games(mainclass.clone())?;
remove_from_workspace(dir.clone())?;
remove_from_toybox_cargo(dir.clone(), game.clone())?;
remove_from_librs(mainclass.clone())?;
delete_project_files(dir.clone())?;
return Ok(());
}

add_to_games(mainclass.clone())?;
add_to_workspace(dir.clone())?;
Expand Down
2 changes: 1 addition & 1 deletion tb_pong/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extern crate lazy_static;
pub mod pong;
pub mod types;

pub use types::PongConfig;
pub use types::Pong;

/// Use 2d types shared with tb_breakout.
pub use toybox_core::body2d::Body2D;
Expand Down
10 changes: 5 additions & 5 deletions tb_pong/src/pong.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ mod screen {
pub const BALL_START_VELOCITY: (i32, i32) = (-3, 1);
}

impl Default for PongConfig {
impl Default for Pong {
fn default() -> Self {
PongConfig {
Pong {
p1_color: Color::rgb(92, 186, 92),
p2_color: Color::rgb(231, 130, 74),
bg_color: Color::rgb(144, 72, 17),
Expand All @@ -47,7 +47,7 @@ impl Default for PongConfig {
}
}

impl toybox_core::Simulation for PongConfig {
impl toybox_core::Simulation for Pong {
fn reset_seed(&mut self, _seed: u32) {
// No randomness in Pong.
}
Expand Down Expand Up @@ -92,7 +92,7 @@ impl toybox_core::Simulation for PongConfig {
serde_json::to_string(self).unwrap()
}
fn from_json(&self, json: &str) -> Result<Box<dyn toybox_core::Simulation>, serde_json::Error> {
Ok(Box::new(serde_json::from_str::<PongConfig>(json)?))
Ok(Box::new(serde_json::from_str::<Pong>(json)?))
}
/// Sync with [ALE Impl](https://github.com/mgbellemare/Arcade-Learning-Environment/blob/master/src/games/supported/Pong.cpp#L47)
/// Note, leaving a call to sort in this impl to remind users that these vecs are ordered!
Expand All @@ -114,7 +114,7 @@ impl toybox_core::Simulation for PongConfig {
serde_json::to_string(&schema).expect("JSONSchema should be flawless.")
}
fn schema_for_config(&self) -> String {
let schema = schema_for!(PongConfig);
let schema = schema_for!(Pong);
serde_json::to_string(&schema).expect("JSONSchema should be flawless.")
}
}
Expand Down
4 changes: 2 additions & 2 deletions tb_pong/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use toybox_core::graphics::Color;

/// This represents the setup needed for a game of Pong.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PongConfig {
pub struct Pong {
/// What is the background color of the board? Brownish by default.
pub bg_color: Color,
/// The gray area at the top/bottom we refer to as the "frame".
Expand Down Expand Up @@ -43,7 +43,7 @@ pub struct FrameState {
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct State {
/// Setup that created this game; effectively immutable here.
pub config: PongConfig,
pub config: Pong,
/// All the data known about this frame.
pub state: FrameState,
}
2 changes: 1 addition & 1 deletion tb_spaceinvaders/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "space_invaders"
name = "spaceinvaders"
version = "0.1.0"
authors = ["John Foley <jfoley@cs.umass.edu>"]
edition = "2018"
Expand Down
Loading