mirror of
https://github.com/LukasKalbertodt/programmieren-in-rust.git
synced 2024-11-18 02:48:58 +01:00
Add sheet3
https://www.youtube.com/watch?v=Z0GFRcFm-aY #election #murica
This commit is contained in:
parent
d84eb733f9
commit
a8234b040a
7
aufgaben/sheet3/README.md
Executable file
7
aufgaben/sheet3/README.md
Executable 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
94
aufgaben/sheet3/task1/README.md
Executable 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
51
aufgaben/sheet3/task1/rule90.rs
Executable 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
163
aufgaben/sheet3/task2/README.md
Executable 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
446
aufgaben/sheet3/task2/poki.rs
Executable 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!"),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user