From f5aaa7c1d6fb3b5f11e19f66fab4d263f394f80b Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Wed, 19 Jul 2017 21:06:19 -0700 Subject: [PATCH 1/9] Make Room struct public so game builds against nightly. --- workshops/src/textadventure/src/board.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/workshops/src/textadventure/src/board.rs b/workshops/src/textadventure/src/board.rs index 9b1259bc..ed7796d5 100644 --- a/workshops/src/textadventure/src/board.rs +++ b/workshops/src/textadventure/src/board.rs @@ -10,13 +10,7 @@ use std::io; // 5 by 5 room game board pub type Board = [[Room; 5]; 5]; -enum Wall { - Solid, - Opening, - Magical { word: String } -} - -struct Room { +pub struct Room { north: Wall, east: Wall, south: Wall, @@ -24,6 +18,12 @@ struct Room { contents: Vec } +enum Wall { + Solid, + Opening, + Magical { word: String } +} + // A hard-coded maze definition. A maze generator would be a lot cooler. // Since the game board is modeled as a 2D array for simplicity walls are // double-sided so their types need to match up across adjacent rooms. From 7a0ef7a24d0fc4f93338ab2ee228a3c3cee77d7b Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Wed, 19 Jul 2017 22:09:44 -0700 Subject: [PATCH 2/9] Add task list with checkboxes. --- workshops/src/textadventure/README.md | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/workshops/src/textadventure/README.md b/workshops/src/textadventure/README.md index 311e7056..4afb702d 100644 --- a/workshops/src/textadventure/README.md +++ b/workshops/src/textadventure/README.md @@ -17,7 +17,7 @@ Here's what we have to work with: **Characters** - We will have 3 kinds of characters. + You the adventurous **Explorer**, wandering from room to room. -+ **Leprechauns** the good wee people with a mischievous sense of humour. They can give you gold or **Magic Words** but you never know if they are telling the truth. They can teleport to any room. ++ **Leprechauns** the good wee people with a mischievous sense of humor. They can give you gold or **Magic Words** but you never know if they are telling the truth. They can teleport to any room. + **Gnomes** who guard the treasures of the earth and just might take your stuff if you’re not careful. They can go one space in any direction - they know the magic word to walk through any kind of wall. **Things** - Things can be picked up and exchanged by characters. @@ -33,32 +33,32 @@ Here's what we have to work with: + **Teleport** allows you to teleport to some other room if you have a teleporter. + Each move consumes 1 food unit and teleporting consumes 5. -Much of the game has already been implemented for you but is not very playable. Your job is to implement the remaining TODOs so that the game plays according to the rules listed above. All the Rust code for the game is in the src directory. The code already compiles and runs although there are numerous compiler warnings. These warnings should disappear as you build out the rest of the game. Look at the game loop in main.rs to get an idea of how the game is structured then begin adding your missing code to the players, board and inventory modules. +Much of the game has already been implemented for you but is not very playable. Your job is to implement the remaining **TODO**s so that the game plays according to the rules listed above. All the Rust code for the game is in the **src** directory. The code already compiles and runs although there are numerous compiler warnings. These warnings should disappear as you build out the rest of the game. Look at the game loop in **main.rs** to get an idea of how the game is structured then begin adding your missing code to the **players**, **board** and **inventory** modules. -As you add more of your code to your game use Cargo to check it and ensure that it still compiles. Run the game from the command line to try out new features as you implement them. For how to get started with Rust and its Cargo tool see [Chapter 2](http://rust-lang.github.io/book/second-edition/ch02-00-guessing-game-tutorial.html) of [*The Rust Programming Language*](http://rust-lang.github.io/book/second-edition) book. Other good resources for learning Rust syntax and semantics are [*Rust by Example*](http://rustbyexample.com) and [*Rustlings*](https://github.com/carols10cents/rustlings) project. +As you add more of your code to your game use Cargo to check it and ensure that it still compiles. Run the game from the command line to try out new features as you implement them. For how to get started with Rust and its Cargo tool see [Chapter 2](http://rust-lang.github.io/book/second-edition/ch02-00-guessing-game-tutorial.html) of [*The Rust Programming Language*](http://rust-lang.github.io/book/second-edition) book. Other good resources for learning Rust syntax and semantics are [*Rust by Example*](http://rustbyexample.com) and the [*Rustlings*](https://github.com/carols10cents/rustlings) project. Here is the order I recommend implementing the remaining features: -+ First implement the is_opening function in board.rs. +- [ ] First implement the `is_opening` function in **board.rs**. -+ Next implement the move_exp_north, move_exp_east, move_exp_south and move_exp_west explorer navigation functions in players.rs. See move_gnome for the mechanics of how these work. Keep in mind that unlike move_gnome an explorer's navigation functions are invoked interactively by the user and an explorer can only walk through openings or magical walls. I wouldn't worry about magical walls or secret words yet. Just treat any magical walls as solid for now. Don't forget that moving one space or walking into a wall consume 1 unit of an explorer's energy per turn. +- [ ] Next implement the `move_exp_north`, `move_exp_east`, `move_exp_south` and `move_exp_west` explorer navigation functions in **players.rs**. See `move_gnome` for the mechanics of how these work. Keep in mind that unlike `move_gnome` an explorer's navigation functions are invoked interactively by the user and an explorer can only walk through openings or magical walls. I wouldn't worry about magical walls or secret words yet. Just treat any magical walls as solid for now. Don't forget that moving one space or walking into a wall consume 1 unit of an explorer's energy per turn. -+ Now implement the teleport_lep and teleport_exp functions in players.rs. See move_gnome for how to move an NPC at random. Unlike gnomes which always move only one space every turn teleporting can put a player anywhere on the board. The teleport_exp function returns a bool because an explorer can only teleport if she has a teleporter. See the build_players function to get an idea of what an explorer's things are. Don't forget that teleporting consumes 5 units of an explorer's energy. Leprechauns are tireless so they don't lose any energy. +- [ ] Now implement the `teleport_lep` and `teleport_exp` functions in **players.rs**. See `move_gnome` for how to move an NPC at random. Unlike gnomes which always move only one space every turn teleporting can put a player anywhere on the board. The teleport_exp function returns a **bool** because an explorer can only teleport if she has a teleporter. See the `build_players` function to get an idea of what an explorer's things are. Don't forget that teleporting consumes 5 units of an explorer's energy. Leprechauns are tireless so they don't lose any energy. -+ Next implement the exp_has_torch function in inventory.rs and the room_has_torch function in board.rs. Now the explorer can see the contents of rooms since she is already carrying a torch. Similarly, if an explorer has no torch but enters a room containing one she can pick up that torchh because the room is already lit by it. +- [ ] Next implement the `exp_has_torch` function in **inventory.rs** and the `room_has_torch` function in **board.rs**. Now the explorer can see the contents of rooms since she is already carrying a torch. Similarly, if an explorer has no torch but enters a room containing one she can pick up that torch because the room is already lit by it. -+ Now implement the exp_pick_up_food, exp_pick_up_coins, exp_pick_up_teleporter and exp_pick_up_torch functions in board.rs. These functions remove things from a room's contents and add it to the explorer's things. An explorer does not put down the things she picks up but she can use coins to pay gnomes for passage or have all her things stolen if she cannot or refuses to pay. +- [ ] Now implement the `exp_pick_up_food`, `exp_pick_up_coins`, `exp_pick_up_teleporter` and `exp_pick_up_torch` functions in **board.rs**. These functions remove things from a room's contents and add it to the explorer's things. An explorer does not put down the things she picks up but she can use coins to pay gnomes for passage or have all her things stolen if she cannot or refuses to pay. -+ Next implement the exp_eat_food function in board.rs. This functions removes all food items from an explorer's things and adds their energy values to the explorer's energy. An explorer eats any food as soon as she picks it up instead of carrying it around. Food is destroyed by eating it and is not replenished. Otherwise the game would never end. +- [ ] Next implement the `exp_eat_food` function in **board.rs**. This function removes all food items from an explorer's things and adds their energy values to the explorer's energy. An explorer eats any food as soon as she picks it up instead of carrying it around. Food is destroyed by eating it and is not replenished. Otherwise the game would never end. -+ Now implement the shake_down function in inventory.rs. The inventory module defines the encounters or exchanges between players. These exchanges happen when players are occupants of the same room. A gnome will demand gold coins from an explorer for safe passage through a room. If the explorer cannot or chooses not to pay the gnome will take all of the explorer's things. Hence the name of the function. An explorer can pay a gnome with fake coins but that only works once. That same gnome will always rob the explorer of everything on the next encounter. This function should be highly interactive and decomposed into smaller functions. See exp_scavenge in board.rs for ideas. +- [ ] Now implement the `shake_down` function in **inventory.rs**. The **inventory** module defines the encounters or exchanges between players. These exchanges happen when players are occupants of the same room. A gnome will demand gold coins from an explorer for safe passage through a room. If the explorer cannot or chooses not to pay the gnome will take all of the explorer's things. Hence the name of the function. An explorer can pay a gnome with fake coins but that only works once. That same gnome will always rob the explorer of everything on the next encounter. This function should be highly interactive and decomposed into smaller functions. See `exp_scavenge` in **board.rs** for ideas. -+ Next implement the trick_or_treat function in inventory.rs. A leprechaun will give an explorer coins or secret words when encountering them. Concentrate on just coins for now. A leprechaun starts out with a finite number of gold and fake coins of various denominations. Those coins are shuffled at creation time so a leprechaun should be able to hand them out one-at-a-time in whatever order they're in. +- [ ] Next implement the `trick_or_treat` function in **inventory.rs**. A leprechaun will give an explorer coins or secret words when encountering her. Concentrate on just coins for now. A leprechaun starts out with a finite number of gold and fake coins of various denominations. Those coins are shuffled at creation time so a leprechaun should be able to hand them out one-at-a-time in whatever order they're in. -+ Now implement the gnome_scavenge function in board.rs. This function makes the game more challenging by enabling gnomes to compete with the explorer for items scattered across the board. A gnome now picks up all the same things that an explorer picks up in exp_scavenge but it does so automatically rather than interactively because it is an NPC. Like exp_scavenge, gnome_scavenge can be decomposed into several smaller functions if needbe. The names and signatures of these smaller functions have not been defined for you. If you really want to get fancy you can have gnomes stockpile gold and other non-perishable items in certain rooms so that an explorer can pick them up again. +- [ ] Now implement the `gnome_scavenge` function in **board.rs**. This function makes the game more challenging by enabling gnomes to compete with the explorer for items scattered across the board. A gnome now picks up all the same things that an explorer does in `exp_scavenge` but it does so automatically rather than interactively because it is an NPC. Like `exp_scavenge`, `gnome_scavenge` can be decomposed into several smaller functions if need be. The names and signatures of these smaller functions have not been defined for you. If you really want to get fancy you can have gnomes stockpile gold and other non-perishable items in certain rooms so that an explorer can pick them up again. -+ Next implement the all_magic_words and all_fake_words functions and finish the trick_or_treat function in inventory.rs. The first 2 functions supply the leprechaun with magic and fake secret words to give to the explorer. The list of all magic words can be generated by iterating through all the walls in the game board and filtering out all the walls that aren't magical. The secret words can then be extracted from the remaining magical walls. The list of all fake words can be whatever you want as long as it excludes the real magic words from the board. Make sure it is apparent what secret word is being given to an explorer when trick_or_treat executes. +- [ ] Next implement the `all_magic_words` and `all_fake_words` functions and finish the `trick_or_treat` function in **inventory.rs**. The first 2 functions supply the leprechaun with magic and fake secret words to give to the explorer. The list of all magic words can be generated by iterating through all the walls in the game board and filtering out all the walls that aren't magical. The secret words can then be extracted from the remaining magical walls. The list of all fake words can be whatever you want as long as it excludes the real magic words from the board. Make sure it is apparent what secret word is being given to an explorer when trick_or_treat executes. -+ Lastly implement the means for an explorer to walk through magical walls. There are 3 magical walls placed strategically throughout the board for you to test this feature out on. While an explorer has no knowledge of what walls are solid or magical you can spot which walls are magical by looking closely at the ASCII rendition of the board. Modify the 4-way explorer navigation functions in players.rs so that if an explorer walks into a wall the user is queried for a secret word that she must type out in its entirety. Implement the has_word function to search a collection of things for said word. If said word is found among the explorer's things then call the open_sesame function in board.rs with that word. In addition to a secret word string, open_sesame also takes an explorer's source and target positions on the board. If the wall an explorer is attempting to traverse is magical and the secret word matches then open_sesame returns true and the explorer is allowed to pass through the wall. +- [ ] Lastly implement the means for an explorer to walk through magical walls. There are 3 magical walls placed strategically throughout the board for you to test this feature out on. While an explorer has no knowledge of what walls are solid or magical you can spot which walls are magical by looking closely at the ASCII rendition of the board. Modify the 4-way explorer navigation functions in **players.rs** so that if an explorer walks into a wall the user is queried for a secret word that she must type out in its entirety. Implement the `has_word` function to search a collection of things for said word. If said word is found among the explorer's things then call the `open_sesame` function in **board.rs** with that word. In addition to a secret word string, `open_sesame` also takes an explorer's source and target positions on the board. If the wall an explorer is attempting to traverse is magical and the secret word matches then `open_sesame` returns **true** and the explorer is allowed to pass through the wall. - The original rules for this game came from a homework assignment for an introductory C++ class taught by [Jim Peckol](http://www.ee.washington.edu/people/jim-peckol) at the University of Washington. That class emphasized UML and object-oriented design. For this Rust workshop, I chose to implement the game in a more functional style using enums and pattern matching for dispatching. Rust is a multi-paradigm language so the same results could have been achieved using traits and polymorphism. See Liz Baillie's [*text-based adventure game*](https://github.com/tildeio/learning-rust) for an example of such an alternate approach. + The original rules for this game are from a homework assignment devised by [Dr. Jim Peckol](http://www.ee.washington.edu/people/jim-peckol) at the University of Washington. This game was the final project for his introductory C++ class that emphasized UML and object-oriented design. For this Rust workshop, I chose to implement the game in a more functional style using enums and pattern matching for dispatching. Rust is a multi-paradigm language so the same results could have been achieved using traits and polymorphism. See Liz Baillie's [*text-based adventure game*](https://github.com/tildeio/learning-rust) for an example of such an alternate approach. From 51abccdd3e80813635d7470294a597447818b331 Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Wed, 19 Jul 2017 22:45:12 -0700 Subject: [PATCH 3/9] Explain how to use checkboxes. --- workshops/src/textadventure/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workshops/src/textadventure/README.md b/workshops/src/textadventure/README.md index 4afb702d..e0fd0821 100644 --- a/workshops/src/textadventure/README.md +++ b/workshops/src/textadventure/README.md @@ -37,7 +37,7 @@ Much of the game has already been implemented for you but is not very playable. As you add more of your code to your game use Cargo to check it and ensure that it still compiles. Run the game from the command line to try out new features as you implement them. For how to get started with Rust and its Cargo tool see [Chapter 2](http://rust-lang.github.io/book/second-edition/ch02-00-guessing-game-tutorial.html) of [*The Rust Programming Language*](http://rust-lang.github.io/book/second-edition) book. Other good resources for learning Rust syntax and semantics are [*Rust by Example*](http://rustbyexample.com) and the [*Rustlings*](https://github.com/carols10cents/rustlings) project. -Here is the order I recommend implementing the remaining features: +If you forked this repo on GitHub you can check the following tasks off as you complete them by editing this **README** as you make your commmits. This **README.md** markdown file is in the **textadventure** directory above the **src** directory. Here is the order I recommend implementing the remaining features: - [ ] First implement the `is_opening` function in **board.rs**. From 1d050f7ca12a22ce0c1016894699f8fcdd1a14f3 Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Fri, 4 Aug 2017 01:01:52 -0700 Subject: [PATCH 4/9] Move explorer and leprechaun. --- workshops/src/textadventure/src/board.rs | 26 ++++++++--- workshops/src/textadventure/src/inventory.rs | 6 +++ workshops/src/textadventure/src/players.rs | 45 ++++++++++++++++---- 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/workshops/src/textadventure/src/board.rs b/workshops/src/textadventure/src/board.rs index ed7796d5..0957eb1d 100644 --- a/workshops/src/textadventure/src/board.rs +++ b/workshops/src/textadventure/src/board.rs @@ -27,24 +27,25 @@ enum Wall { // A hard-coded maze definition. A maze generator would be a lot cooler. // Since the game board is modeled as a 2D array for simplicity walls are // double-sided so their types need to match up across adjacent rooms. +// FIXME ensure walls match across adjacent rooms pub fn build_board() -> Board { use self::Wall::*; use inventory::Thing::*; [[Room {north: Solid, east: Solid, south: Opening, west: Solid, contents: vec![]}, - Room {north: Solid, east: Opening, south: Opening, west: Opening, + Room {north: Solid, east: Opening, south: Opening, west: Solid, contents: vec![]}, Room {north: Solid, east: Opening, south: Solid, west: Opening, contents: vec![GoldCoin { denom: 5 }, GoldCoin { denom: 10 }]}, Room {north: Solid, east: Opening, south: Solid, west: Opening, contents: vec![]}, - Room {north: Solid, east: Solid, south: Opening, west: Solid, + Room {north: Solid, east: Solid, south: Opening, west: Opening, contents: vec![Food { name: String::from("chicken"), energy: 8 }]} ], [Room {north: Opening, east: Opening, south: Magical { word: String::from("aberto") }, west: Solid, contents: vec![]}, - Room {north: Magical { word: String::from("aberto") }, east: Solid, south: Solid, west: Opening, + Room {north: Opening, east: Solid, south: Solid, west: Opening, contents: vec![Torch, Food { name: String::from("ham"), energy: 9 }]}, Room {north: Solid, east: Opening, south: Opening, west: Solid, contents: vec![]}, @@ -53,7 +54,7 @@ pub fn build_board() -> Board { Room {north: Opening, east: Solid, south: Opening, west: Opening, contents: vec![GoldCoin { denom: 10 }, GoldCoin { denom: 25 }]} ], - [Room {north: Solid, east: Opening, south: Opening, west: Solid, + [Room {north: Magical { word: String::from("aberto") }, east: Opening, south: Opening, west: Solid, contents: vec![]}, Room {north: Solid, east: Solid, south: Opening, west: Opening, contents: vec![GoldCoin { denom: 10 }, GoldCoin { denom: 10 }]}, @@ -187,9 +188,22 @@ pub fn scavenge(player: Player, board: &mut Board) -> Player { _player } -// TODO pub fn is_opening(room: &Position, wall: &players::Direction, board: &Board) -> bool { - false + let _room = pos_to_room(room, board); + + let is_opening = |wall: &Wall| { + match *wall { + Wall::Opening => true, + _ => false + } + }; + + match *wall { + players::Direction::North => is_opening(&_room.north), + players::Direction::East => is_opening(&_room.east), + players::Direction::South => is_opening(&_room.south), + players::Direction::West => is_opening(&_room.west) + } } // TODO diff --git a/workshops/src/textadventure/src/inventory.rs b/workshops/src/textadventure/src/inventory.rs index a1cf91d8..380da5dc 100644 --- a/workshops/src/textadventure/src/inventory.rs +++ b/workshops/src/textadventure/src/inventory.rs @@ -36,6 +36,12 @@ pub fn display_exp_things(exp: &ExplorerData) { println!() } +// FIXME Add stub and mention in README +// TODO +pub fn exp_has_teleporter(exp: &ExplorerData) -> bool { + false +} + // TODO pub fn exp_has_torch(exp: &ExplorerData) -> bool { false diff --git a/workshops/src/textadventure/src/players.rs b/workshops/src/textadventure/src/players.rs index 913881ce..e2dc61a3 100644 --- a/workshops/src/textadventure/src/players.rs +++ b/workshops/src/textadventure/src/players.rs @@ -183,12 +183,13 @@ fn move_exp(data: ExplorerData, board: &Board) -> ExplorerData { _data } +// FIXME invert North and South fn dir_to_dx_dy(direction: &Direction) -> (i32, i32) { use self::Direction::*; match *direction { - North => (0, 1), - South => (0, -1), + North => (0, -1), + South => (0, 1), East => (1, 0), West => (-1, 0) } @@ -225,29 +226,55 @@ fn move_lep(data: LeprechaunData, board: &Board) -> LeprechaunData { _data } -// TODO fn teleport_lep(data: &mut LeprechaunData, board: &Board) { + let (dx, dy) : (i32, i32); + + dx = rand::thread_rng().gen_range(0, board[0].len() as i32); + dy = rand::thread_rng().gen_range(0, board.len() as i32); + data.pos = Position::new(dx, dy, board) } -// TODO fn teleport_exp(data: &mut ExplorerData, board: &Board) -> bool { - false + if inventory::exp_has_teleporter(data) { + let (dx, dy) : (i32, i32); + + dx = rand::thread_rng().gen_range(0, board[0].len() as i32); + dy = rand::thread_rng().gen_range(0, board.len() as i32); + data.pos = Position::new(dx, dy, board); + data.energy -= 5; + + true + } else { + false + } +} + +fn move_exp_direction(data: &mut ExplorerData, board: &Board, direction: Direction) { + let (dx, dy) = dir_to_dx_dy(&direction); + let is_in_bounds = board::move_in_bounds(&data.pos, &dx, &dy, board); + let is_opening = board::is_opening(&data.pos, &direction, board); + + if is_in_bounds && is_opening { + data.pos = board::move_pos(data.pos, dx, dy, board) + } + + data.energy -= 1 } -// TODO fn move_exp_north(data: &mut ExplorerData, board: &Board) { + move_exp_direction(data, board, Direction::North) } -// TODO fn move_exp_south(data: &mut ExplorerData, board: &Board) { + move_exp_direction(data, board, Direction::South) } -// TODO fn move_exp_east(data: &mut ExplorerData, board: &Board) { + move_exp_direction(data, board, Direction::East) } -// TODO fn move_exp_west(data: &mut ExplorerData, board: &Board) { + move_exp_direction(data, board, Direction::West) } // TODO From 1243ed8a66f2d0052361943d01413cca14cfe0ac Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Tue, 8 Aug 2017 21:27:26 -0700 Subject: [PATCH 5/9] inventory-related functions --- workshops/src/textadventure/src/board.rs | 136 +++++++++++++++---- workshops/src/textadventure/src/inventory.rs | 30 +++- workshops/src/textadventure/src/players.rs | 21 +++ 3 files changed, 153 insertions(+), 34 deletions(-) diff --git a/workshops/src/textadventure/src/board.rs b/workshops/src/textadventure/src/board.rs index 0957eb1d..25c406f1 100644 --- a/workshops/src/textadventure/src/board.rs +++ b/workshops/src/textadventure/src/board.rs @@ -10,6 +10,7 @@ use std::io; // 5 by 5 room game board pub type Board = [[Room; 5]; 5]; +#[derive(Clone, Debug)] pub struct Room { north: Wall, east: Wall, @@ -18,6 +19,13 @@ pub struct Room { contents: Vec } +impl Room { + fn pick_up_thing(&mut self, index: usize) -> Thing { + self.contents.remove(index) + } +} + +#[derive(Clone, Debug)] enum Wall { Solid, Opening, @@ -94,7 +102,7 @@ pub fn display_map(board: &Board, players: &Players) { for room in board[0].iter() { match room.north { Wall::Solid => print!(" ----"), - Wall::Magical{ref word} => print!(" ++++"), + Wall::Magical{ .. } => print!(" ++++"), Wall::Opening => print!(" ") } } @@ -104,7 +112,7 @@ pub fn display_map(board: &Board, players: &Players) { if col == 0 { match board[row][col].west { Wall::Solid => print!("|"), - Wall::Magical { ref word } => print!("+"), + Wall::Magical { .. } => print!("+"), Wall::Opening => print!(" ") } } @@ -120,7 +128,7 @@ pub fn display_map(board: &Board, players: &Players) { }; match board[row][col].east { Wall::Solid => print!(" {} |", character), - Wall::Magical { ref word } => print!(" {} +", character), + Wall::Magical { .. } => print!(" {} +", character), Wall::Opening => print!(" {} ", character) } } @@ -128,7 +136,7 @@ pub fn display_map(board: &Board, players: &Players) { for room in board[row].iter() { match room.south { Wall::Solid => print!(" ----"), - Wall::Magical { ref word } => print!(" ++++"), + Wall::Magical { .. } => print!(" ++++"), Wall::Opening => print!(" ") } } @@ -188,8 +196,8 @@ pub fn scavenge(player: Player, board: &mut Board) -> Player { _player } -pub fn is_opening(room: &Position, wall: &players::Direction, board: &Board) -> bool { - let _room = pos_to_room(room, board); +pub fn is_opening(pos: &Position, direction: &players::Direction, board: &Board) -> bool { + let room = pos_to_room(pos, board); let is_opening = |wall: &Wall| { match *wall { @@ -198,11 +206,11 @@ pub fn is_opening(room: &Position, wall: &players::Direction, board: &Board) -> } }; - match *wall { - players::Direction::North => is_opening(&_room.north), - players::Direction::East => is_opening(&_room.east), - players::Direction::South => is_opening(&_room.south), - players::Direction::West => is_opening(&_room.west) + match *direction { + players::Direction::North => is_opening(&room.north), + players::Direction::East => is_opening(&room.east), + players::Direction::South => is_opening(&room.south), + players::Direction::West => is_opening(&room.west) } } @@ -211,8 +219,8 @@ pub fn open_sesame(word: &String, source: &Position, target: &Position, board: & false } -fn pos_to_room<'a, 'b>(pos: &'a Position, board: &'b Board) -> &'b Room { - &board[pos.y as usize][pos.x as usize] +fn pos_to_room(pos: &Position, board: &Board) -> Room { + board[pos.y as usize][pos.x as usize].clone() } fn display_room_contents(room: &Room) { @@ -252,7 +260,7 @@ fn exp_scavenge(data: ExplorerData, board: &mut Board) -> ExplorerData { while !empty { inventory::display_exp_things(&exp); - display_room_contents(pos_to_room(&pos, board)); + display_room_contents(&pos_to_room(&pos, board)); println!("Enter letter command: Pick up [F]ood [C]oins tele[P]orter [T]orch or [D]one"); @@ -268,7 +276,7 @@ fn exp_scavenge(data: ExplorerData, board: &mut Board) -> ExplorerData { match input.trim().to_uppercase().chars().nth(0) { Some(command) => { match command { - 'F' => { exp_pick_up_food(&mut exp, board); exp_eat_food(&mut exp) }, + 'F' => { exp_pick_up_food(&mut exp, board); players::exp_eat_food(&mut exp) }, 'C' => exp_pick_up_coins(&mut exp, board), 'P' => exp_pick_up_teleporter(&mut exp, board), 'T' => exp_pick_up_torch(&mut exp, board), @@ -294,6 +302,13 @@ fn last_occupant<'a, 'b>(pos: &'a Position, players: &'b Players) -> Option<&'b .last() } +fn pick_up_thing(board: &Board, pos: &Position, index: usize) -> (Thing, Room) { + let mut room = board[pos.y as usize][pos.x as usize].clone(); + let thing = room.pick_up_thing(index); + + (thing, room) +} + // TODO fn gnome_scavenge(data: GnomeData, board: &mut Board) -> GnomeData { let mut gnome = data; @@ -303,32 +318,93 @@ fn gnome_scavenge(data: GnomeData, board: &mut Board) -> GnomeData { gnome } -// TODO fn room_has_torch(pos: &Position, board: &Board) -> bool { - false + let room = pos_to_room(pos, board); + + let torch = + room.contents.iter() + .find(|&thing| { + match *thing { + Thing::Torch => true, + _ => false + } + }); + + match torch { + Some(_) => true, + None => false + } } -// TODO fn exp_pick_up_food(exp: &mut ExplorerData, board: &mut Board) { - println!("Feature not implemented.") -} - -// TODO -fn exp_eat_food(exp: &mut ExplorerData) { - println!("Feature not implemented.") + let pos = players::get_exp_pos(exp); + let room = pos_to_room(&pos, board); + + room.contents.iter() + .position(|thing| { + match *thing { + Thing::Food {..} => true, + _ => false + } + }) + .map(|index| { + let (food, room) = pick_up_thing(board, &pos, index); + exp.things.push(food); + board[pos.y as usize][pos.x as usize] = room + }); } -// TODO fn exp_pick_up_coins(exp: &mut ExplorerData, board: &mut Board) { - println!("Feature not implemented.") + let pos = players::get_exp_pos(exp); + let room = pos_to_room(&pos, board); + + room.contents.iter() + .position(|thing| { + match *thing { + Thing::FakeCoin {..} => true, + Thing::GoldCoin {..} => true, + _ => false + } + }) + .map(|index| { + let (coin, room) = pick_up_thing(board, &pos, index); + exp.things.push(coin); + board[pos.y as usize][pos.x as usize] = room + }); } -// TODO fn exp_pick_up_teleporter(exp: &mut ExplorerData, board: &mut Board) { - println!("Feature not implemented.") + let pos = players::get_exp_pos(exp); + let room = pos_to_room(&pos, board); + + room.contents.iter() + .position(|thing| { + match *thing { + Thing::Teleporter {..} => true, + _ => false + } + }) + .map(|index| { + let (teleporter, room) = pick_up_thing(board, &pos, index); + exp.things.push(teleporter); + board[pos.y as usize][pos.x as usize] = room + }); } -// TODO fn exp_pick_up_torch(exp: &mut ExplorerData, board: &mut Board) { - println!("Feature not implemented.") + let pos = players::get_exp_pos(exp); + let room = pos_to_room(&pos, board); + + room.contents.iter() + .position(|thing| { + match *thing { + Thing::Torch {..} => true, + _ => false + } + }) + .map(|index| { + let (torch, room) = pick_up_thing(board, &pos, index); + exp.things.push(torch); + board[pos.y as usize][pos.x as usize] = room + }); } diff --git a/workshops/src/textadventure/src/inventory.rs b/workshops/src/textadventure/src/inventory.rs index 380da5dc..f3803ccd 100644 --- a/workshops/src/textadventure/src/inventory.rs +++ b/workshops/src/textadventure/src/inventory.rs @@ -37,14 +37,36 @@ pub fn display_exp_things(exp: &ExplorerData) { } // FIXME Add stub and mention in README -// TODO pub fn exp_has_teleporter(exp: &ExplorerData) -> bool { - false + let teleporter = + exp.things.iter() + .find(|&thing| { + match *thing { + Thing::Teleporter => true, + _ => false + } + }); + + match teleporter { + Some(_) => true, + None => false + } } -// TODO pub fn exp_has_torch(exp: &ExplorerData) -> bool { - false + let torch = + exp.things.iter() + .find(|&thing| { + match *thing { + Thing::Torch => true, + _ => false + } + }); + + match torch { + Some(_) => true, + None => false + } } // TODO diff --git a/workshops/src/textadventure/src/players.rs b/workshops/src/textadventure/src/players.rs index e2dc61a3..a5bf89f7 100644 --- a/workshops/src/textadventure/src/players.rs +++ b/workshops/src/textadventure/src/players.rs @@ -132,6 +132,27 @@ pub fn get_lep_pos(data: &LeprechaunData) -> Position { data.pos } +pub fn exp_eat_food(exp: &mut ExplorerData) { + exp.things.iter() + .position(|thing| { + match *thing { + Thing::Food {..} => true, + _ => false + } + }) + .map(|index| { + let food = exp.things.remove(index); + + match food { + Thing::Food { name, energy } => { + println!("Explorer ate {:?} increasing energy by {:?}", name, energy); + exp.energy += energy + }, + _ => () + } + }); +} + fn is_explorer(player: &Player) -> bool { match *player { Player::Explorer(_) => true, From 051f315e46225312215dbdba186e67d6bc888936 Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Tue, 8 Aug 2017 22:53:27 -0700 Subject: [PATCH 6/9] Provide a pick_up_thing helper function. --- workshops/src/textadventure/README.md | 18 ++-- workshops/src/textadventure/src/board.rs | 104 +++---------------- workshops/src/textadventure/src/inventory.rs | 31 +----- workshops/src/textadventure/src/players.rs | 75 ++----------- 4 files changed, 36 insertions(+), 192 deletions(-) diff --git a/workshops/src/textadventure/README.md b/workshops/src/textadventure/README.md index e0fd0821..2c2567d4 100644 --- a/workshops/src/textadventure/README.md +++ b/workshops/src/textadventure/README.md @@ -39,17 +39,19 @@ As you add more of your code to your game use Cargo to check it and ensure that If you forked this repo on GitHub you can check the following tasks off as you complete them by editing this **README** as you make your commmits. This **README.md** markdown file is in the **textadventure** directory above the **src** directory. Here is the order I recommend implementing the remaining features: -- [ ] First implement the `is_opening` function in **board.rs**. +- [ ] First implement the `is_opening` function in **board.rs**. You should use the `pos_to_room` function that is already provided for you to get at the wall in question. -- [ ] Next implement the `move_exp_north`, `move_exp_east`, `move_exp_south` and `move_exp_west` explorer navigation functions in **players.rs**. See `move_gnome` for the mechanics of how these work. Keep in mind that unlike `move_gnome` an explorer's navigation functions are invoked interactively by the user and an explorer can only walk through openings or magical walls. I wouldn't worry about magical walls or secret words yet. Just treat any magical walls as solid for now. Don't forget that moving one space or walking into a wall consume 1 unit of an explorer's energy per turn. +- [ ] Next implement the `move_exp_direction` function in **players.rs** so the explorer can move. See `move_gnome` for the mechanics of how this function should work. Keep in mind that unlike `move_gnome` the direction of an explorer's move is chosen interactively by the user and an explorer can only walk through openings or magical walls. I wouldn't worry about magical walls or secret words yet. Just treat any magical walls as solid for now. Don't forget that moving one space or walking into a wall consume 1 unit of an explorer's energy per turn. -- [ ] Now implement the `teleport_lep` and `teleport_exp` functions in **players.rs**. See `move_gnome` for how to move an NPC at random. Unlike gnomes which always move only one space every turn teleporting can put a player anywhere on the board. The teleport_exp function returns a **bool** because an explorer can only teleport if she has a teleporter. See the `build_players` function to get an idea of what an explorer's things are. Don't forget that teleporting consumes 5 units of an explorer's energy. Leprechauns are tireless so they don't lose any energy. +- [ ] Now implement the `teleport_lep` and `teleport_exp` functions in **players.rs**. See `move_gnome` for how to move an NPC at random. Unlike gnomes which always move only one space every turn teleporting can put a player anywhere on the board. The `teleport_exp` function returns a **bool** because an explorer can only teleport if she has a teleporter. See the `build_players` function to get an idea of what an explorer's things are. Don't forget that teleporting consumes 5 units of an explorer's energy. Leprechauns are tireless so they don't lose any energy. -- [ ] Next implement the `exp_has_torch` function in **inventory.rs** and the `room_has_torch` function in **board.rs**. Now the explorer can see the contents of rooms since she is already carrying a torch. Similarly, if an explorer has no torch but enters a room containing one she can pick up that torch because the room is already lit by it. +- [ ] Next implement the `exp_has_teleporter` function in **inventory.rs**. Take a look at the [any method](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any) of Rust's Iterator trait. -- [ ] Now implement the `exp_pick_up_food`, `exp_pick_up_coins`, `exp_pick_up_teleporter` and `exp_pick_up_torch` functions in **board.rs**. These functions remove things from a room's contents and add it to the explorer's things. An explorer does not put down the things she picks up but she can use coins to pay gnomes for passage or have all her things stolen if she cannot or refuses to pay. +- [ ] Now implement the `exp_has_torch` function in **inventory.rs** and the `room_has_torch` function in **board.rs**. Once that is done the explorer can see the contents of rooms since she is already carrying a torch. Similarly, if an explorer has no torch but enters a room containing one she can pick up that torch because the room is already lit by it. -- [ ] Next implement the `exp_eat_food` function in **board.rs**. This function removes all food items from an explorer's things and adds their energy values to the explorer's energy. An explorer eats any food as soon as she picks it up instead of carrying it around. Food is destroyed by eating it and is not replenished. Otherwise the game would never end. +- [ ] Next implement the `exp_pick_up_food`, `exp_pick_up_coins`, `exp_pick_up_teleporter` and `exp_pick_up_torch` functions in **board.rs**. These functions remove things from a room's contents and add it to the explorer's things. Use the `get_exp_pos`, `pos_to_room` and `pick_up_thing` functions that are already provided for you. Don't forget to assign the new room returned by `pick_up_thing` back to its position in the `board` array otherwise the thing[s] being picked up will remain in the room. An explorer does not put down the things she picks up but she can use coins to pay gnomes for passage or have all her things stolen if she cannot or refuses to pay. + +- [ ] Now implement the `exp_eat_food` function in **players.rs**. This function removes all food items from an explorer's things and adds their energy values to the explorer's energy. An explorer eats any food as soon as she picks it up instead of carrying it around. Food is destroyed by eating it and is not replenished. Otherwise the game would never end. - [ ] Now implement the `shake_down` function in **inventory.rs**. The **inventory** module defines the encounters or exchanges between players. These exchanges happen when players are occupants of the same room. A gnome will demand gold coins from an explorer for safe passage through a room. If the explorer cannot or chooses not to pay the gnome will take all of the explorer's things. Hence the name of the function. An explorer can pay a gnome with fake coins but that only works once. That same gnome will always rob the explorer of everything on the next encounter. This function should be highly interactive and decomposed into smaller functions. See `exp_scavenge` in **board.rs** for ideas. @@ -57,8 +59,8 @@ If you forked this repo on GitHub you can check the following tasks off as you c - [ ] Now implement the `gnome_scavenge` function in **board.rs**. This function makes the game more challenging by enabling gnomes to compete with the explorer for items scattered across the board. A gnome now picks up all the same things that an explorer does in `exp_scavenge` but it does so automatically rather than interactively because it is an NPC. Like `exp_scavenge`, `gnome_scavenge` can be decomposed into several smaller functions if need be. The names and signatures of these smaller functions have not been defined for you. If you really want to get fancy you can have gnomes stockpile gold and other non-perishable items in certain rooms so that an explorer can pick them up again. -- [ ] Next implement the `all_magic_words` and `all_fake_words` functions and finish the `trick_or_treat` function in **inventory.rs**. The first 2 functions supply the leprechaun with magic and fake secret words to give to the explorer. The list of all magic words can be generated by iterating through all the walls in the game board and filtering out all the walls that aren't magical. The secret words can then be extracted from the remaining magical walls. The list of all fake words can be whatever you want as long as it excludes the real magic words from the board. Make sure it is apparent what secret word is being given to an explorer when trick_or_treat executes. +- [ ] Next implement the `all_magic_words` and `all_fake_words` functions and finish the `trick_or_treat` function in **inventory.rs**. The first 2 functions supply the leprechaun with magic and fake secret words to give to the explorer. The list of all magic words can be generated by iterating through all the walls in the game board and filtering out all the walls that aren't magical. The secret words can then be extracted from the remaining magical walls. The list of all fake words can be whatever you want as long as it excludes the real magic words from the board. Make sure it is apparent what secret word is being given to an explorer when `trick_or_treat` executes. -- [ ] Lastly implement the means for an explorer to walk through magical walls. There are 3 magical walls placed strategically throughout the board for you to test this feature out on. While an explorer has no knowledge of what walls are solid or magical you can spot which walls are magical by looking closely at the ASCII rendition of the board. Modify the 4-way explorer navigation functions in **players.rs** so that if an explorer walks into a wall the user is queried for a secret word that she must type out in its entirety. Implement the `has_word` function to search a collection of things for said word. If said word is found among the explorer's things then call the `open_sesame` function in **board.rs** with that word. In addition to a secret word string, `open_sesame` also takes an explorer's source and target positions on the board. If the wall an explorer is attempting to traverse is magical and the secret word matches then `open_sesame` returns **true** and the explorer is allowed to pass through the wall. +- [ ] Lastly implement the means for an explorer to walk through magical walls. There are 3 magical walls placed strategically throughout the board for you to test this feature out on. While an explorer has no knowledge of what walls are solid or magical you can spot which walls are magical by looking closely at the ASCII rendition of the board. Modify the `move_exp_direction` function in **players.rs** so that if an explorer walks into a wall the user is queried for a secret word that she must type out in its entirety. Implement the `has_word` function to search a collection of things for said word. If said word is found among the explorer's things then call the `open_sesame` function in **board.rs** with that word. In addition to a secret word string, `open_sesame` also takes an explorer's source and target positions on the board. If the wall an explorer is attempting to traverse is magical and the secret word matches then `open_sesame` returns **true** and the explorer is allowed to pass through the wall. The original rules for this game are from a homework assignment devised by [Dr. Jim Peckol](http://www.ee.washington.edu/people/jim-peckol) at the University of Washington. This game was the final project for his introductory C++ class that emphasized UML and object-oriented design. For this Rust workshop, I chose to implement the game in a more functional style using enums and pattern matching for dispatching. Rust is a multi-paradigm language so the same results could have been achieved using traits and polymorphism. See Liz Baillie's [*text-based adventure game*](https://github.com/tildeio/learning-rust) for an example of such an alternate approach. diff --git a/workshops/src/textadventure/src/board.rs b/workshops/src/textadventure/src/board.rs index 25c406f1..c5aed660 100644 --- a/workshops/src/textadventure/src/board.rs +++ b/workshops/src/textadventure/src/board.rs @@ -35,7 +35,6 @@ enum Wall { // A hard-coded maze definition. A maze generator would be a lot cooler. // Since the game board is modeled as a 2D array for simplicity walls are // double-sided so their types need to match up across adjacent rooms. -// FIXME ensure walls match across adjacent rooms pub fn build_board() -> Board { use self::Wall::*; use inventory::Thing::*; @@ -196,22 +195,9 @@ pub fn scavenge(player: Player, board: &mut Board) -> Player { _player } +// TODO pub fn is_opening(pos: &Position, direction: &players::Direction, board: &Board) -> bool { - let room = pos_to_room(pos, board); - - let is_opening = |wall: &Wall| { - match *wall { - Wall::Opening => true, - _ => false - } - }; - - match *direction { - players::Direction::North => is_opening(&room.north), - players::Direction::East => is_opening(&room.east), - players::Direction::South => is_opening(&room.south), - players::Direction::West => is_opening(&room.west) - } + false } // TODO @@ -318,93 +304,27 @@ fn gnome_scavenge(data: GnomeData, board: &mut Board) -> GnomeData { gnome } +// TODO fn room_has_torch(pos: &Position, board: &Board) -> bool { - let room = pos_to_room(pos, board); - - let torch = - room.contents.iter() - .find(|&thing| { - match *thing { - Thing::Torch => true, - _ => false - } - }); - - match torch { - Some(_) => true, - None => false - } + false } +// TODO fn exp_pick_up_food(exp: &mut ExplorerData, board: &mut Board) { - let pos = players::get_exp_pos(exp); - let room = pos_to_room(&pos, board); - - room.contents.iter() - .position(|thing| { - match *thing { - Thing::Food {..} => true, - _ => false - } - }) - .map(|index| { - let (food, room) = pick_up_thing(board, &pos, index); - exp.things.push(food); - board[pos.y as usize][pos.x as usize] = room - }); + println!("Feature not implemented.") } +// TODO fn exp_pick_up_coins(exp: &mut ExplorerData, board: &mut Board) { - let pos = players::get_exp_pos(exp); - let room = pos_to_room(&pos, board); - - room.contents.iter() - .position(|thing| { - match *thing { - Thing::FakeCoin {..} => true, - Thing::GoldCoin {..} => true, - _ => false - } - }) - .map(|index| { - let (coin, room) = pick_up_thing(board, &pos, index); - exp.things.push(coin); - board[pos.y as usize][pos.x as usize] = room - }); + println!("Feature not implemented.") } +// TODO fn exp_pick_up_teleporter(exp: &mut ExplorerData, board: &mut Board) { - let pos = players::get_exp_pos(exp); - let room = pos_to_room(&pos, board); - - room.contents.iter() - .position(|thing| { - match *thing { - Thing::Teleporter {..} => true, - _ => false - } - }) - .map(|index| { - let (teleporter, room) = pick_up_thing(board, &pos, index); - exp.things.push(teleporter); - board[pos.y as usize][pos.x as usize] = room - }); + println!("Feature not implemented.") } +// TODO fn exp_pick_up_torch(exp: &mut ExplorerData, board: &mut Board) { - let pos = players::get_exp_pos(exp); - let room = pos_to_room(&pos, board); - - room.contents.iter() - .position(|thing| { - match *thing { - Thing::Torch {..} => true, - _ => false - } - }) - .map(|index| { - let (torch, room) = pick_up_thing(board, &pos, index); - exp.things.push(torch); - board[pos.y as usize][pos.x as usize] = room - }); + println!("Feature not implemented.") } diff --git a/workshops/src/textadventure/src/inventory.rs b/workshops/src/textadventure/src/inventory.rs index f3803ccd..56d2f229 100644 --- a/workshops/src/textadventure/src/inventory.rs +++ b/workshops/src/textadventure/src/inventory.rs @@ -36,37 +36,14 @@ pub fn display_exp_things(exp: &ExplorerData) { println!() } -// FIXME Add stub and mention in README +// TODO pub fn exp_has_teleporter(exp: &ExplorerData) -> bool { - let teleporter = - exp.things.iter() - .find(|&thing| { - match *thing { - Thing::Teleporter => true, - _ => false - } - }); - - match teleporter { - Some(_) => true, - None => false - } + return false } +// TODO pub fn exp_has_torch(exp: &ExplorerData) -> bool { - let torch = - exp.things.iter() - .find(|&thing| { - match *thing { - Thing::Torch => true, - _ => false - } - }); - - match torch { - Some(_) => true, - None => false - } + return false } // TODO diff --git a/workshops/src/textadventure/src/players.rs b/workshops/src/textadventure/src/players.rs index a5bf89f7..b1d1de53 100644 --- a/workshops/src/textadventure/src/players.rs +++ b/workshops/src/textadventure/src/players.rs @@ -132,25 +132,9 @@ pub fn get_lep_pos(data: &LeprechaunData) -> Position { data.pos } +// TODO pub fn exp_eat_food(exp: &mut ExplorerData) { - exp.things.iter() - .position(|thing| { - match *thing { - Thing::Food {..} => true, - _ => false - } - }) - .map(|index| { - let food = exp.things.remove(index); - - match food { - Thing::Food { name, energy } => { - println!("Explorer ate {:?} increasing energy by {:?}", name, energy); - exp.energy += energy - }, - _ => () - } - }); + println!("Feature not implemented.") } fn is_explorer(player: &Player) -> bool { @@ -186,10 +170,10 @@ fn move_exp(data: ExplorerData, board: &Board) -> ExplorerData { match input.trim().to_uppercase().chars().nth(0) { Some(command) => { match command { - 'N' => { move_exp_north(&mut _data, board); break }, - 'S' => { move_exp_south(&mut _data, board); break }, - 'E' => { move_exp_east(&mut _data, board); break }, - 'W' => { move_exp_west(&mut _data, board); break }, + 'N' => { move_exp_direction(&mut _data, board, Direction::North); break }, + 'S' => { move_exp_direction(&mut _data, board, Direction::South); break }, + 'E' => { move_exp_direction(&mut _data, board, Direction::East); break }, + 'W' => { move_exp_direction(&mut _data, board, Direction::West); break }, 'T' => if teleport_exp(&mut _data, board) { break } else { println!("Cannot teleport") }, _ => println!("Invalid command") @@ -204,7 +188,6 @@ fn move_exp(data: ExplorerData, board: &Board) -> ExplorerData { _data } -// FIXME invert North and South fn dir_to_dx_dy(direction: &Direction) -> (i32, i32) { use self::Direction::*; @@ -247,55 +230,17 @@ fn move_lep(data: LeprechaunData, board: &Board) -> LeprechaunData { _data } +// TODO fn teleport_lep(data: &mut LeprechaunData, board: &Board) { - let (dx, dy) : (i32, i32); - - dx = rand::thread_rng().gen_range(0, board[0].len() as i32); - dy = rand::thread_rng().gen_range(0, board.len() as i32); - data.pos = Position::new(dx, dy, board) } +// TODO fn teleport_exp(data: &mut ExplorerData, board: &Board) -> bool { - if inventory::exp_has_teleporter(data) { - let (dx, dy) : (i32, i32); - - dx = rand::thread_rng().gen_range(0, board[0].len() as i32); - dy = rand::thread_rng().gen_range(0, board.len() as i32); - data.pos = Position::new(dx, dy, board); - data.energy -= 5; - - true - } else { - false - } + false } +// TODO fn move_exp_direction(data: &mut ExplorerData, board: &Board, direction: Direction) { - let (dx, dy) = dir_to_dx_dy(&direction); - let is_in_bounds = board::move_in_bounds(&data.pos, &dx, &dy, board); - let is_opening = board::is_opening(&data.pos, &direction, board); - - if is_in_bounds && is_opening { - data.pos = board::move_pos(data.pos, dx, dy, board) - } - - data.energy -= 1 -} - -fn move_exp_north(data: &mut ExplorerData, board: &Board) { - move_exp_direction(data, board, Direction::North) -} - -fn move_exp_south(data: &mut ExplorerData, board: &Board) { - move_exp_direction(data, board, Direction::South) -} - -fn move_exp_east(data: &mut ExplorerData, board: &Board) { - move_exp_direction(data, board, Direction::East) -} - -fn move_exp_west(data: &mut ExplorerData, board: &Board) { - move_exp_direction(data, board, Direction::West) } // TODO From 74cf16d9175d1d2a9c1f0c91417590c6b7944bb2 Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Tue, 8 Aug 2017 23:42:22 -0700 Subject: [PATCH 7/9] return is implicit at the end of a function --- workshops/src/textadventure/src/inventory.rs | 4 ++-- workshops/src/textadventure/src/main.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/workshops/src/textadventure/src/inventory.rs b/workshops/src/textadventure/src/inventory.rs index 56d2f229..660818e9 100644 --- a/workshops/src/textadventure/src/inventory.rs +++ b/workshops/src/textadventure/src/inventory.rs @@ -38,12 +38,12 @@ pub fn display_exp_things(exp: &ExplorerData) { // TODO pub fn exp_has_teleporter(exp: &ExplorerData) -> bool { - return false + false } // TODO pub fn exp_has_torch(exp: &ExplorerData) -> bool { - return false + false } // TODO diff --git a/workshops/src/textadventure/src/main.rs b/workshops/src/textadventure/src/main.rs index 4a6c8409..9c814474 100644 --- a/workshops/src/textadventure/src/main.rs +++ b/workshops/src/textadventure/src/main.rs @@ -22,5 +22,5 @@ fn main() { } } - println!("GAME OVER"); + println!("GAME OVER") } From 48a4debf92cffa430bfd7b36a284ad2edc3646cd Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Tue, 19 Sep 2017 16:57:53 -0700 Subject: [PATCH 8/9] name change in README --- workshops/src/textadventure/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workshops/src/textadventure/README.md b/workshops/src/textadventure/README.md index 2c2567d4..5cb8588a 100644 --- a/workshops/src/textadventure/README.md +++ b/workshops/src/textadventure/README.md @@ -63,4 +63,4 @@ If you forked this repo on GitHub you can check the following tasks off as you c - [ ] Lastly implement the means for an explorer to walk through magical walls. There are 3 magical walls placed strategically throughout the board for you to test this feature out on. While an explorer has no knowledge of what walls are solid or magical you can spot which walls are magical by looking closely at the ASCII rendition of the board. Modify the `move_exp_direction` function in **players.rs** so that if an explorer walks into a wall the user is queried for a secret word that she must type out in its entirety. Implement the `has_word` function to search a collection of things for said word. If said word is found among the explorer's things then call the `open_sesame` function in **board.rs** with that word. In addition to a secret word string, `open_sesame` also takes an explorer's source and target positions on the board. If the wall an explorer is attempting to traverse is magical and the secret word matches then `open_sesame` returns **true** and the explorer is allowed to pass through the wall. - The original rules for this game are from a homework assignment devised by [Dr. Jim Peckol](http://www.ee.washington.edu/people/jim-peckol) at the University of Washington. This game was the final project for his introductory C++ class that emphasized UML and object-oriented design. For this Rust workshop, I chose to implement the game in a more functional style using enums and pattern matching for dispatching. Rust is a multi-paradigm language so the same results could have been achieved using traits and polymorphism. See Liz Baillie's [*text-based adventure game*](https://github.com/tildeio/learning-rust) for an example of such an alternate approach. + The original rules for this game are from a homework assignment devised by [Dr. Jim Peckol](http://www.ee.washington.edu/people/jim-peckol) at the University of Washington. This game was the final project for his introductory C++ class that emphasized UML and object-oriented design. For this Rust workshop, I chose to implement the game in a more functional style using enums and pattern matching for dispatching. Rust is a multi-paradigm language so the same results could have been achieved using traits and polymorphism. See Lee Baillie's [*text-based adventure game*](https://github.com/tildeio/learning-rust) for an example of such an alternate approach. From 681aa8b3492e66cb4ea47d3f90b110b1482c427a Mon Sep 17 00:00:00 2001 From: Frank Vasquez Date: Thu, 15 Mar 2018 18:31:26 -0700 Subject: [PATCH 9/9] Remove unused mods and root section of Cargo.lock. --- Cargo.lock | 22 ++++++++++---------- workshops/src/textadventure/src/inventory.rs | 2 -- workshops/src/textadventure/src/players.rs | 8 +++---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66f96fe8..b1d53a2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,3 @@ -[root] -name = "url_shortener" -version = "0.1.0" -dependencies = [ - "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nickel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "adler32" version = "1.0.0" @@ -1608,6 +1597,17 @@ dependencies = [ "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "url_shortener" +version = "0.1.0" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nickel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusqlite 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "user32-sys" version = "0.1.2" diff --git a/workshops/src/textadventure/src/inventory.rs b/workshops/src/textadventure/src/inventory.rs index 660818e9..a2d85493 100644 --- a/workshops/src/textadventure/src/inventory.rs +++ b/workshops/src/textadventure/src/inventory.rs @@ -5,10 +5,8 @@ use players::ExplorerData; use players::GnomeData; use players::LeprechaunData; use players::Direction; -use board; use board::Board; use board::Position; -use std::io; #[derive(Clone, PartialEq, Eq, Debug)] pub enum Thing { diff --git a/workshops/src/textadventure/src/players.rs b/workshops/src/textadventure/src/players.rs index b1d1de53..3d5a76a7 100644 --- a/workshops/src/textadventure/src/players.rs +++ b/workshops/src/textadventure/src/players.rs @@ -138,15 +138,15 @@ pub fn exp_eat_food(exp: &mut ExplorerData) { } fn is_explorer(player: &Player) -> bool { - match *player { - Player::Explorer(_) => true, + match player { + &Player::Explorer(_) => true, _ => false } } fn is_dead(player: &Player) -> bool { - match *player { - Player::Explorer(ref data) => data.energy <= 0, + match player { + &Player::Explorer(ref data) => data.energy <= 0, _ => false } }