Add sheet3

https://www.youtube.com/watch?v=Z0GFRcFm-aY #election #murica
This commit is contained in:
Lukas Kalbertodt 2016-11-09 01:15:26 +01:00
parent d84eb733f9
commit a8234b040a
5 changed files with 761 additions and 0 deletions

7
aufgaben/sheet3/README.md Executable file
View File

@ -0,0 +1,7 @@
Blatt 3
=======
Wie beim letzten (und allen noch folgenden) Blättern soll die Bearbeitung
dieser Aufgaben auf einem anderen Git-Branch geschehen.
Legt dazu einen Branch `sheet3` an, von welchem ihr später den PR öffnet.
Wichtig: Stellt sicher, dass ihr wirklich auf eurem neuen Branch seid!

94
aufgaben/sheet3/task1/README.md Executable file
View File

@ -0,0 +1,94 @@
Aufgabe 3.1: Rule 90
====================
Zelluläre Automaten simulieren ein System von Zellen mit diskreten Werten in
diskreten Zeitschritten, wobei sich der Zustand des Systems zum Zeitpunkt
*n + 1* deterministisch aus dem Zustand zum Zeitpunkt *n* berechnen lässt.
In den meisten Fällen hängt der Wert einer Zelle allein von seinem eigenen Wert
und den Werten seiner Nachbarn zum vorherigen Zeitpunkt ab.
["Rule 90"][1] ist ein eindimensionaler, zellulärer Automat, dessen Zellen
boolesch sind, also nur zwei Werte annehmen können.
Oft spricht man bei einer Zelle mit einer 0 von einer "toten Zelle", bei einer
1 von einer "lebenden" Zelle.
Diese Zellen können nun nach bestimmten Vorschriften in diskreten Zeitschritten
simuliert werden. Der Wert einer Zelle zu dem Zeitschritt *n + 1* hängt von
seinem Wert und der Wert seiner beiden direkten Nachbarn zum Zeitpunkt *n* ab.
Sind z.B. zum Zeitpunkt *n* alle drei Zellen tot, wird die mittlere der Zellen
zum Zeitpunkt *n + 1* ebenfalls tot bleiben.
Die vollständigen Regeln lauten wie folgt:
| Vorheriger Zeitschritt | `111` | `110` | `101` | `100` | `011` | `010` | `001` | `000` |
| ----------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| Neuer Wert für mittlere Zelle | `_0_` | `_1_` | `_0_` | `_1_` | `_1_` | `_0_` | `_1_` | `_0_` |
Nun kann man die diskreten Zeitschritte zeilenweise anzeigen. Das sieht dann
z.B. so aus (in diesem Beispiel ist der Automat 5 Zellen groß):
```
0 1 1 0 0
1 1 1 1 0
1 0 0 1 0
0 1 1 0 0
```
Zu diesem Zeitpunkt sollte man sich fragen: Was ist mit den Randzellen?
In dieser Aufgabe gehen wir davon aus, dass der Anfang und das Ende des Systems
verbunden sind, d.h. der linke Nachbar der ersten Zelle ist die letzte Zelle
und der rechte Nachbar der letzten Zelle ist die erste Zelle.
---
In dieser Aufgabe sollt ihr ein Programm schreiben, welches den Automaten
"Rule 90" für Nutzereingaben simuliert. Diese Funktionalität soll in mehrere
Funktionen aufgeteilt werden.
### a) Eingabe einlesen
Zunächst muss vom Nutzer die initiale Konfiguration des Automaten eingelesen
werden.
Dafür soll eine Funktion `read_input()` geschrieben werden, die teilweise
bereits von uns bereitgestellt wurde.
In dem schon existierenden Code wird lediglich ein String vom Nutzer
eingelesen, der nur '0' und '1' enthält.
Eure Aufgabe ist es, aus diesem String einen `Vec<bool>` zu erstellen, der für
jede '0' im String einen `false`-Eintrag und für jede '1' einen `true`-Eintrag
enthält (in der richtigen Reihenfolge).
Dieser Vektor soll dann von der Funktion zurückgegeben werden.
### b) Zeitschritt simulieren
Nun sollt ihr eine Funktion `next_step()` schreiben, die aus der Konfiguration
des Automaten zu einem Zeitpunkt den nächsten Zeitpunkt berechnen soll.
Den Automaten zu einem Zeitpunkt stellen wir weiterhin einfach als ein
Boolean-Array dar; es muss also kein neuer Typ erstellt werden.
### c) Funktionen in `main()` benutzen
Nun müssen beide Funktionen noch genutzt werden.
In der `main()` Funktion wird eine initiale Konfiguration des Automaten
eingelesen, welcher dann über z.B. 20 Zeitschritte simuliert werden soll.
In jedem Zeitschritt soll der Zustand in einer Zeile auf dem Terminal
ausgegeben werden. Die Ausgabe kann entweder in Form von 0en und 1en geschehen
(wie oben im Beispiel) oder ihr könnt andere eindeutige Zeichen verwenden.
Es würde sich z.B. anbieten, für tote Zellen zwei Leerzeichen und für lebende
Zellen zwei U+2588 FULL BLOCK ('█') auszugeben:
```
████
████████
██ ██
████
```
Wenn alles klappt, probiert mal die initiale Konfiguration
`0000000000000001000000000000000` aus!
[1]: https://en.wikipedia.org/wiki/Rule_90

51
aufgaben/sheet3/task1/rule90.rs Executable file
View File

@ -0,0 +1,51 @@
//! Task 3.1: Rule 90
fn main() {
// TODO: Task 1c)
}
/// Reads a valid initial configuration for our automaton from the terminal.
fn read_input() -> Vec<bool> {
// This tries to read a string from the terminal, checks whether it's
// valid (only contains 1's and 0's). If the user fails to input a correct
// string, this routine will ask again until the user finally manages to
// give us a correct string.
//
// You don't need to understand this routine yet; that's why I've written
// it already ;-)
//
// You only need to use the `input` variable (of type `String`). You can
// also assume that it only contains '0' and '1' chars.
let input = {
let mut buffer = String::new();
loop {
println!("Please give me the initial configuration (a string of '0' and '1'!):");
buffer.clear();
// `read_line` returns an error if the input isn't valid UTF8 or if
// a strange IO error occured. We just panic in that case...
std::io::stdin()
.read_line(&mut buffer)
.expect("something went seriously wrong :O");
if buffer.trim().chars().all(|c| c == '1' || c == '0') {
break;
}
}
buffer.trim().to_string()
};
// TODO: Task 1a)
}
// TODO: Task 1b)
#[test]
fn rule90_rules() {
assert_eq!(next_step(&[false, false, false]), vec![false, false, false]);
assert_eq!(next_step(&[ true, false, false]), vec![false, true, true]);
assert_eq!(next_step(&[ true, true, false]), vec![ true, true, false]);
assert_eq!(next_step(&[ true, true, true]), vec![false, false, false]);
}

163
aufgaben/sheet3/task2/README.md Executable file
View File

@ -0,0 +1,163 @@
Aufgabe 3.2: Pokemon
====================
In dieser Aufgabe soll ein kleines, Terminal-basiertes Pokemon-Spiel
programmiert werden.
Dieses Spiel werden wir in den nächsten Übungszetteln stetig erweitern.
Um direkt mal etwas zu zeigen: *In etwa* so wird das Ergebnis dieser Aufgabe
aussehen:
```
Player Red, please choose a Pokemon (or type '?' to get a complete list)
?
#001 Bulbasaur
#002 Ivysaur
#003 Venusaur
#004 Charmander
#005 Charmeleon
#006 Charizard
#007 Squirtle
#008 Wartortle
#009 Blastoise
Player Red, please choose a Pokemon (or type '?' to get a complete list)
Charmander
Player Blue, please choose a Pokemon (or type '?' to get a complete list)
Wartortle
>>>>> Status: Charmander has 18 HP, Wartortle has 20 HP
>>>>> Charmander is about to attack! Which move shall it execute?
0: Tackle
!!! Please give me the attack ID:
0
>>>>> Charmander uses Tackle! (Wartortle has 15 HP left)
Wartortle is about to attack! Which move shall it execute?
0: Tackle
1: Water Gun
!!! Please give me the attack ID:
1
>>>>> Wartortle uses Water Gun! (Charmander has 7 HP left)
>>>>> Status: Charmander has 7 HP, Wartortle has 15 HP
Charmander is about to attack! Which move shall it execute?
0: Tackle
!!! Please give me the attack ID:
0
>>>>> Charmander uses Tackle! (Wartortle has 10 HP left)
Wartortle is about to attack! Which move shall it execute?
0: Tackle
1: Water Gun
!!! Please give me the attack ID:
0
>>>>> Wartortle uses Tackle! (Charmander has 1 HP left)
>>>>> Status: Charmander has 1 HP, Wartortle has 10 HP
Charmander is about to attack! Which move shall it execute?
0: Tackle
!!! Please give me the attack ID:
0
>>>>> Charmander uses Tackle! (Wartortle has 5 HP left)
Wartortle is about to attack! Which move shall it execute?
0: Tackle
1: Water Gun
!!! Please give me the attack ID:
1
>>>>> Wartortle uses Water Gun! (Charmander has 0 HP left)
>>>>> Charmander fainted!
```
## Umfang und Art der Aufgabe
In dieser Aufgabe wird bereits recht viel Code bereitgestellt und insgesamt
wird die Lösung eher lang. Derzeit ist der komplette Code noch in einer Datei;
das wird sich aber in der nächsten Woche ändern, wenn wir Module kennenlernen.
Trotzdem soll diese Aufgabe schulen, sich in Rust-Code zurechtzufinden. Falls
einige Sachen unklar sein sollten, zögert nicht, auf Piazza zu fragen! Viele
gute Fragen auf Piazza helfen auch den anderen.
Außerdem geht es in dieser Aufgabe natürlich um Pokemon. Ich habe versucht,
alles so zu formulieren, dass es auch Menschen verstehen, die nie etwas mit
Pokemon am Hut hatten. Falls mir das irgendwo nicht gelungen ist, sagt mir
bitte Bescheid oder fragt direkt auf Piazza. "Pokemon" stand nämlich nicht als
Voraussetzung in der Kursbeschreibung :P
Aber grob beschrieben: In Pokemon gibt es komische Tiere/Monster, die "Pokemon"
genannt werden. Diese Pokemon können von Menschen gefangen werden, meist um
damit gegen andere "Pokemon-Trainer" zu kämpfen. Es gibt unterschiedliche
Pokemon und unterschiedliche Attacken.
In der bereitgestellten Datei gibt es bereits diversen Code. Davon besteht ein
größerer Teil nur aus Konstanten. Das sind einfach Daten über Pokemon und
Attacken, die nicht weiter verstanden werden müssen. Wichtig ist aber, dass
all diese Daten direkt in der Executable gespeichert sind und daher immer die
`'static` Lifetime haben.
## a) Pokemon wählen lassen
In diesem Teil geht es darum, die beiden Spieler zu fragen, welches Pokemon sie
benutzen möchten. Dazu sollt ihr ein paar Funktionen erstellen:
- `print_pokemon_list`: Druckt eine Liste aller verfügbaren Pokemon (in der
`POKEDEX` Konstante gespeichert) auf dem Terminal.
- `find_pokemon_by_name`: Bekommt einen Namen und sucht nach einem PokemonModel
mit diesem Namen im `POKEDEX`. Gibt das PokemonModel zurück, oder `None`,
wenn der Name nicht gefunden wurde. Die Suche darf gerne in O(n) sein.
- `choose_pokemon`: Fordert den Spieler auf, den Namen eines Pokemon einzugeben.
Bietet außerdem an, alle Pokemon aufzulisten, wenn der Spieler '?' eingibt.
Diese Funktion liefert dann ein PokemonModel zurück (falls der Spieler
einen ungültigen Namen eingibt, erneut auffordern, einen richtigen Namen
einzugeben). Hierfür ist die Funktion `read_string()` nützlich!
Wenn diese Funktionen funktionieren, soll `choose_pokemon()` in der `main()`-
Funktion aufgerufen werden, sodass beide Spieler ein Pokemon wählen können.
## b) `Pokemon` Typ implementieren
Bis jetzt gibt es nur einen Typ `PokemonModel`, der globale Eigenschaften von
einer Pokemon-Art speichert (wie z.B.: "Pferde haben vier Beine").
Allerdings brauchen wir noch einen Typ, der eine Instanz eines Pokemon
darstellt (wie z.B. "dieses Tier ist ein Pferd und hat braunes Fell").
Dazu soll ein neuer Typ `Pokemon` angelegt werden.
Dieser Typ speichert sich folgende Daten:
- Zu welcher Pokemon-Art er gehört (Referenz auf ein `PokemonModel`; wichtig:
alle PokemonModels haben eine `'static` Lifetime).
- Die derzeitigen "stats" (Typ `Stats`), also z.B. auch die HP
- Das derzeitige Level (mögliche Level: 1 bis 100)
Der Typ soll folgende Funktionen und Methoden besitzen:
- Konstruktor-Funktion `with_level`: Gegeben wird ein PokemonModel und ein
Level, eine gültige `Pokemon`-Instanz soll zurückgegeben werden. Hinweis:
Nützlich ist die Funktoin `Stats::at_level()`.
- Getter-Methoden (in Rust verzichtet man auf `get_`, also nicht `get_foo`,
sondern oft nur `foo`):
- `stats()`
- `model()`
- `name() `
- `level()`
- `is_alive()`
- `endure_attack()`: Bekommt eine Referenz auf ein anderes Pokemon und auf
eine Attacke. Abhängig davon werden die HP von dem jetzigen Pokemon
angepasst. Ihr braucht die Funktion `attack_damage()` dafür.
## c) Kampfsystem
Zuletzt müssen wir nur noch das eigentliche Kampfsystem bauen. Dazu sollen
abwechselnd beide Spieler aufgefordert werden, eine Attacke auszusuchen.
Diese Attacke wird dann vom Pokemon ausgeführt und so das andere Pokemon
verletzt. Die ungefähren Anforderungen:
- Beide Spieler werden abwechselnd gefragt
- Es werden die verfügbaren Attacken aufgelistet
- Die gewählte Attacke wird ausgeführt
- Der Status beider Pokemon wird regelmäßig angezeigt
- Wenn ein Pokemon stirbt, soll sich das Programm beenden
Euer Programm muss nicht genau so aussehen, wie oben im Beispiel gezeigt.
Bonusaufgabe: Zuerst darf das Pokemon mit dem höheren `speed` Wert angreifen.

446
aufgaben/sheet3/task2/poki.rs Executable file
View File

@ -0,0 +1,446 @@
//! Task 3.2: Pokemon
fn main() {}
/// Describes an attack with all its properties. This type is similar to
/// `PokemonModel`, as there are finite many, immutable instances of this type
/// in a database. This is not a type whose instances change over time.
#[derive(Debug, Clone, Copy)]
struct Attack {
category: AttackCategory,
name: &'static str,
/// Base power of the move. The actual inflicted damage is calculated with
/// a formula using the move's power and a few other parameters.
base_power: u8,
type_: Type,
}
/// Category of an attack.
///
/// Note: currently, the category 'status' is missing.
#[derive(Debug, Clone, Copy)]
enum AttackCategory {
/// Attacks with body contact, like "Tackle" or "Bite"
Physical,
/// Attacks without body contact, like "Bubble Beam" or "Thunderbolt"
Special,
}
/// Describes how effective an attack of one type is on a Pokemon of another
/// type.
///
/// Note that a Pokemon can have two types. In order to determine the
/// effectiveness, the multipliers of the effectivenesses on both types
/// are multiplied. As such, there can be 0.25 and 4.0 multipliers!
#[derive(Debug, Clone, Copy)]
enum TypeEffectiveness {
NotEffective,
NotVeryEffective,
Normal,
SuperEffective,
}
impl TypeEffectiveness {
/// Returns the type effectiveness of an attack from one attacker type
/// on one defender type.
fn of_attack(attacker: Type, defender: Type) -> Self {
use Type::*;
use TypeEffectiveness as Te;
// TODO: complete this
match (attacker, defender) {
(Fire, Water) => Te::NotVeryEffective,
(Fire, Grass) => Te::SuperEffective,
(Water, Fire) => Te::SuperEffective,
(Water, Grass) => Te::NotVeryEffective,
(Grass, Fire) => Te::NotVeryEffective,
(Grass, Water) => Te::SuperEffective,
_ => Te::Normal,
}
}
/// Returns the corresponding multiplier for the damage formula.
fn multiplier(&self) -> f64 {
match *self {
TypeEffectiveness::NotEffective => 0.0,
TypeEffectiveness::NotVeryEffective => 0.5,
TypeEffectiveness::Normal => 1.0,
TypeEffectiveness::SuperEffective => 2.0,
}
}
}
/// Types (sometimes called "elements") of the Pokemon universe. Each
/// attack-move has exactly one type, Pokemons can have one or two types.
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
enum Type {
Normal,
Fire,
Fighting,
Water,
Flying,
Grass,
Poison,
Electric,
Ground,
Psychic,
Rock,
Ice,
Bug,
Dragon,
Ghost,
Dark,
Steel,
Fairy,
}
/// Describes the type of a Pokemon. Pokemon can have one or two types.
#[derive(Debug, Clone, Copy)]
enum PokemonType {
One(Type),
Two(Type, Type),
}
/// Describes a kind of Pokemon, e.g. "Pikachu".
///
/// This is different than an actual, living Pokemon. This struct just
/// describes properties that are the same for every creature of this
/// Pokemon kind.
#[derive(Debug, Clone, Copy)]
struct PokemonModel {
/// Name of the Pokemon
name: &'static str,
/// ID in the international Pokedex
id: u16,
type_: PokemonType,
base_stats: Stats,
/// This is different from the real Pokemon games: attacks are not part
/// of the Pokemon model, but of the Pokemon itself (as they change over
/// time). A pokemon just has an abstract learnset of potential attacks.
/// But this is easier for now.
attacks: &'static [&'static Attack]
}
/// Describes the basic stats of a Pokemon.
///
/// Each living Pokemon has an actual stat value, but each Pokemon kind also
/// has so called "base stats". These base stats are used to calculate the
/// actual stats, whose depend on the Pokemon's current level. Stronger Pokemon
/// have higher base stats.
#[derive(Debug, Clone, Copy)]
struct Stats {
/// Health points
hp: u16,
/// Speed, sometimes called initiative (INIT)
speed: u16,
/// Strength of physical attacks (like "Tackle")
attack: u16,
/// Strength of special attacks (like "Bubble Beam")
special_attack: u16,
/// Defense power against physical attacks (like "Tackle")
defense: u16,
/// Defense power against special attacks (like "Bubble Beam")
special_defense: u16,
}
impl Stats {
/// Given the base stats and a level, this function returns the actual
/// stats for that level.
///
/// This function doesn't implement the correct formula used by Pokemon
/// games. It is a simplified version of the original formula for now: we
/// ignore IVs, EVs and the Pokemon's nature). The complete formula can be
/// found [here (HP)][1] and [here (other stats)][2].
///
/// [1]: http://bulbapedia.bulbagarden.net/wiki/File:HPStatCalcGen34.png
/// [2]: http://bulbapedia.bulbagarden.net/wiki/File:OtherStatCalcGen34.png
fn at_level(base: Self, level: u8) -> Self {
/// The formula is the same for all stats != hp
fn stat_formula(base: u16, level: u8) -> u16 {
((base as f64 * level as f64) / 50.0 + 5.0) as u16
}
let hp = (
(base.hp as f64 * level as f64) / 50.0
+ level as f64
+ 10.0
) as u16;
Stats {
hp: hp,
speed: stat_formula(base.speed, level),
attack: stat_formula(base.attack, level),
special_attack: stat_formula(base.special_attack, level),
defense: stat_formula(base.defense, level),
special_defense: stat_formula(base.special_defense, level),
}
}
}
// ===========================================================================
// ===========================================================================
// ===========================================================================
// Formulas to calculate stuff
// ===========================================================================
// ===========================================================================
// ===========================================================================
/// Calculates the damage of an attack. We don't use the exact formula, but
/// a simplified version of it. In particular, we simplified the "Modifier"
/// term quite a bit. The correct and complete formula can be found [here][1].
///
/// [1]: http://bulbapedia.bulbagarden.net/wiki/Damage#Damage_formula
// fn attack_damage(attacker: &Pokemon, defender: &Pokemon, attack: Attack) -> u16 {
// // Depending on the attack category, get the correct stats
// let (attack_mod, defense_mod) = match attack.category {
// AttackCategory::Physical => {
// (attacker.stats().attack, defender.stats().defense)
// }
// AttackCategory::Special => {
// (attacker.stats().special_attack, defender.stats().special_defense)
// }
// };
// // Cast everything to f64 to reduce noise in actual formula
// let (attack_mod, defense_mod) = (attack_mod as f64, defense_mod as f64);
// let base_power = attack.base_power as f64;
// let attacker_level = attacker.level() as f64;
// // The modifier only depends on the type effectiveness (in our simplified
// // version!).
// let modifier = match defender.model().type_ {
// PokemonType::One(ty) => {
// TypeEffectiveness::of_attack(attack.type_, ty).multiplier()
// }
// PokemonType::Two(ty_a, ty_b) => {
// TypeEffectiveness::of_attack(attack.type_, ty_a).multiplier()
// * TypeEffectiveness::of_attack(attack.type_, ty_b).multiplier()
// }
// };
// // With every parameter prepared above, here is the formula
// (
// (
// ((2.0 * attacker_level + 10.0) / 250.0)
// * (attack_mod / defense_mod)
// * base_power
// + 2.0
// ) * modifier
// ) as u16
// }
// ===========================================================================
// ===========================================================================
// ===========================================================================
// This is just constant data!
// ===========================================================================
// ===========================================================================
// ===========================================================================
/// The Pokemon database!
///
/// Apart from the "attacks" field, all values are correct.
const POKEDEX: &'static [PokemonModel] = &[
PokemonModel {
name: "Bulbasaur",
id: 001,
type_: PokemonType::Two(Type::Poison, Type::Grass),
base_stats: Stats {
hp: 45,
attack: 49,
defense: 49,
special_attack: 65,
special_defense: 65,
speed: 45,
},
attacks: &[TACKLE],
},
PokemonModel {
name: "Ivysaur",
id: 002,
type_: PokemonType::Two(Type::Poison, Type::Grass),
base_stats: Stats {
hp: 60,
attack: 62,
defense: 63,
special_attack: 80,
special_defense: 80,
speed: 60,
},
attacks: &[TACKLE, VINE_WHIP],
},
PokemonModel {
name: "Venusaur",
id: 003,
type_: PokemonType::Two(Type::Poison, Type::Grass),
base_stats: Stats {
hp: 80,
attack: 82,
defense: 83,
special_attack: 100,
special_defense: 100,
speed: 80,
},
attacks: &[TACKLE, VINE_WHIP],
},
PokemonModel {
name: "Charmander",
id: 004,
type_: PokemonType::One(Type::Fire),
base_stats: Stats {
hp: 39,
attack: 52,
defense: 43,
special_attack: 60,
special_defense: 50,
speed: 65,
},
attacks: &[TACKLE],
},
PokemonModel {
name: "Charmeleon",
id: 005,
type_: PokemonType::One(Type::Fire),
base_stats: Stats {
hp: 58,
attack: 64,
defense: 58,
special_attack: 80,
special_defense: 65,
speed: 80,
},
attacks: &[TACKLE, EMBER],
},
PokemonModel {
name: "Charizard",
id: 006,
type_: PokemonType::Two(Type::Fire, Type::Flying),
base_stats: Stats {
hp: 78,
attack: 84,
defense: 78,
special_attack: 109,
special_defense: 85,
speed: 100,
},
attacks: &[TACKLE, EMBER],
},
PokemonModel {
name: "Squirtle",
id: 007,
type_: PokemonType::One(Type::Water),
base_stats: Stats {
hp: 44,
attack: 48,
defense: 65,
special_attack: 50,
special_defense: 64,
speed: 43,
},
attacks: &[TACKLE],
},
PokemonModel {
name: "Wartortle",
id: 008,
type_: PokemonType::One(Type::Water),
base_stats: Stats {
hp: 59,
attack: 63,
defense: 80,
special_attack: 65,
special_defense: 80,
speed: 58,
},
attacks: &[TACKLE, WATER_GUN],
},
PokemonModel {
name: "Blastoise",
id: 009,
type_: PokemonType::One(Type::Water),
base_stats: Stats {
hp: 79,
attack: 83,
defense: 100,
special_attack: 85,
special_defense: 105,
speed: 78,
},
attacks: &[TACKLE, WATER_GUN],
},
];
/// List of all attacks.
///
/// Of course, these are not all attacks. We will probably provide a much
/// bigger database with the next sheet.
const ATTACK_DB: &'static [Attack] = &[
Attack {
category: AttackCategory::Physical,
name: "Tackle",
base_power: 50,
type_: Type::Normal,
},
Attack {
category: AttackCategory::Special,
name: "Vine Whip",
base_power: 45,
type_: Type::Grass,
},
Attack {
category: AttackCategory::Special,
name: "Ember",
base_power: 40,
type_: Type::Fire,
},
Attack {
category: AttackCategory::Special,
name: "Water Gun",
base_power: 40,
type_: Type::Water,
},
];
// These are just some easy names to be more expressive in the Pokedex.
const TACKLE: &'static Attack = &ATTACK_DB[0];
const VINE_WHIP: &'static Attack = &ATTACK_DB[1];
const EMBER: &'static Attack = &ATTACK_DB[2];
const WATER_GUN: &'static Attack = &ATTACK_DB[3];
// ===========================================================================
// ===========================================================================
// ===========================================================================
// Helper functions (you don't need to understand how they work yet)
// ===========================================================================
// ===========================================================================
// ===========================================================================
/// Reads a string from the terminal/user.
fn read_string() -> String {
let mut buffer = String::new();
std::io::stdin()
.read_line(&mut buffer)
.expect("something went horribly wrong...");
// Discard trailing newline
let new_len = buffer.trim_right().len();
buffer.truncate(new_len);
buffer
}
/// Reads a valid `usize` integer from the terminal/user.
fn read_usize() -> usize {
loop {
match read_string().parse() {
Ok(res) => return res,
Err(_) => println!("That was not an integer! Please try again!"),
}
}
}