Commit dd712ffd authored by autopawn's avatar autopawn

Added spacewar activity.

parent 6bbbff8f
{
"configurations": [
{
"type": "java",
"name": "CodeLens (Launch) - Spacewar",
"request": "launch",
"mainClass": "Spacewar",
"projectName": "clases_lp_5d326f63"
}
]
}
\ No newline at end of file
......@@ -35,9 +35,9 @@ typedef struct {
} vectorsum;
```
:cheese: **Hint 2**: [vectorsum.h](activities/vectorsum.h)
:cheese: **Hint 2**: [vectorsum.h](activities/c_vectorsum/vectorsum.h)
:cheese: **Hint 3**: [vectorsum.c](activities/vectorsum.c)
:cheese: **Hint 3**: [vectorsum.c](activities/c_vectorsum/vectorsum.c)
---
......
---
marp: true
size: 4:3
number: true
paginate: true
theme: default
style: |
img[alt~="center"]{display:block;margin: 0 auto;}
---
# :soccer: Actividad
:arrow_right: Programar una simulación de combate entre 2 bandos de **naves espaciales**.
:arrow_right: En cada interación, cada nave decide a qué nave del bando contrario **atacar**. Y lanzan un ataque.
:arrow_right: Al final del turno, todos los ataques se efectúan.
![center h:300px](figures/naves1.png)
---
![center h:300px](figures/naves2.png)
---
:arrow_right: Las naves tienen **escudo** y **salud**.
:arrow_right: Los ataques primero restan **escudo** y luego **salud**.
:arrow_right: Una nave se **destruye** cuando se queda sin salud y no podrá atacar.
:arrow_right: La simulación termina cuando se han destruído todas las naves de al menos uno de los dos bandos.
---
:arrow_right: Hay 2 tipos de naves:
```
SCOUT
- Salud: 3
- Escudos: 2
- Atacan a un enemigo al azar.
- Ataques hacen 2 de daño.
```
```
DESTROYER
- Salud: 5
- Escudos: 5
- Atacan al enemigo más fuerte (salud+escudo)
- Regeneran 1 escudo al principio del turno.
- Ataques hacen 4 de daño.
```
---
```plain
== Team 1 ==
Empire Des1 [5] (7)
Empire Sco1 [3] (2)
Empire Sco2 [3] (2)
== Team 2 ==
Revel Sco1 [3] (2)
Revel Sco2 [3] (2)
Revel Sco3 [3] (2)
Revel Sco4 [3] (2)
== Attacks: ==
Empire Des1 hits Revel Sco1 (4 dmg).
Empire Sco1 hits Revel Sco1 (2 dmg).
Empire Sco2 hits Revel Sco1 (2 dmg).
Revel Sco1 hits Empire Sco2 (2 dmg).
Revel Sco2 hits Empire Sco2 (2 dmg).
Revel Sco3 hits Empire Sco2 (2 dmg).
Revel Sco4 hits Empire Des1 (2 dmg).
```
---
```plain
== Team 1 ==
Empire Des1 [5] (7)
Empire Sco1 [3] (2)
Empire Sco2 [3] (2)
== Team 2 ==
Revel Sco1 [3] (2)
Revel Sco2 [3] (2)
Revel Sco3 [3] (2)
Revel Sco4 [3] (2)
== Attacks: ==
Empire Des1 hits Revel Sco1 (4 dmg).
Empire Sco1 hits Revel Sco1 (2 dmg).
Empire Sco2 hits Revel Sco1 (2 dmg).
Revel Sco1 hits Empire Sco2 (2 dmg).
Revel Sco2 hits Empire Sco2 (2 dmg).
Revel Sco3 hits Empire Sco2 (2 dmg).
Revel Sco4 hits Empire Des1 (2 dmg).
```
---
```plain
== Team 1 ==
Empire Des1 [5] (7)
Empire Sco1 [3] (2)
Empire Sco2 #destroyed#
== Team 2 ==
Revel Sco1 #destroyed#
Revel Sco2 [3] (2)
Revel Sco3 [3] (2)
Revel Sco4 [3] (2)
== Attacks: ==
Empire Des1 hits Revel Sco2 (4 dmg).
Empire Sco1 hits Revel Sco4 (2 dmg).
Revel Sco2 hits Empire Sco1 (2 dmg).
Revel Sco3 hits Empire Des1 (2 dmg).
Revel Sco4 hits Empire Des1 (2 dmg).
```
---
```plain
== Team 1 ==
Empire Des1 [5] (4)
Empire Sco1 [3] (0)
Empire Sco2 #destroyed#
== Team 2 ==
Revel Sco1 #destroyed#
Revel Sco2 [1] (0)
Revel Sco3 [3] (2)
Revel Sco4 [3] (0)
== Attacks: ==
Empire Des1 hits Revel Sco3 (4 dmg).
Empire Sco1 hits Revel Sco2 (2 dmg).
Revel Sco2 hits Empire Des1 (2 dmg).
Revel Sco3 hits Empire Des1 (2 dmg).
Revel Sco4 hits Empire Des1 (2 dmg).
```
---
```plain
== Team 1 ==
Empire Des1 [3] (1)
Empire Sco1 [3] (0)
Empire Sco2 #destroyed#
== Team 2 ==
Revel Sco1 #destroyed#
Revel Sco2 #destroyed#
Revel Sco3 [1] (0)
Revel Sco4 [3] (0)
== Attacks: ==
Empire Des1 hits Revel Sco4 (4 dmg).
Empire Sco1 hits Revel Sco3 (2 dmg).
Revel Sco3 hits Empire Des1 (2 dmg).
Revel Sco4 hits Empire Des1 (2 dmg).
```
---
```plain
== Team 1 ==
Empire Des1 #destroyed#
Empire Sco1 [3] (0)
Empire Sco2 #destroyed#
== Team 2 ==
Revel Sco1 #destroyed#
Revel Sco2 #destroyed#
Revel Sco3 #destroyed#
Revel Sco4 #destroyed#
== Terminated ==
```
---
:arrow_forward: Respecto a los tipos de naves `SCOUT` y `DESTROYER`:
Deberíamos crear 2 clases separadas `Scout` y `Destroyer` o una sola `Ship`:question:
---
### Argumentos a favor de 1 clase:
* Será mucho más conveniente trabajar los grupos de naves como un sólo arreglo `Ship[]`, en vez de dos arreglos `Scout[]` y `Destroyer[]`.
* Hay mucho comportamiento común entre ambas clases, que sería bueno reutilizar.
* Los ataques `Attack` tendrán que apuntar a un `Ship`, sería inconveniente que tengan variables para ambas clases.
---
### Argumentos en contra de 1 clase:
* Los comportamientos de ambos tipos de naves difieren.
Se podría tener un `enum` del tipo de nave como atributo y colocar varios condicionales en los métodos, pero esto sería poco escalable.
:arrow_forward: Sería mejor si hubiera una forma simple de agregar nuevas naves.
* Es poco eficiente que un `Ship` incluso tenga los atributos de las naves que no usa.
:arrow_forward: En definitiva, necesitamos que cada `Ship` difieran en los atributos y **métodos**, pero que se puedan trabajar desde una interfaz **común**.
:arrow_forward: Para eso debemos usar **herencia** y **polimorfismo**.
---
:cheese: Pista:
```java
public abstract class Ship {...}
```
```java
public class Scout extends Ship {...}
```
```java
public class Destroyer extends Ship {...}
```
```java
public class Attack {...}
```
:warning: **Try to do it yourself first**.
\ No newline at end of file
*.class
\ No newline at end of file
public class Attack{
Ship origin;
Ship target;
int damage;
public Attack(Ship origin, Ship target, int damage){
this.origin = origin;
this.target = target;
this.damage = damage;
}
public void execute(){
this.target.receive_damage(this.damage);
}
public String toString(){
return origin.name+" hits "+target.name+" ("+damage+" dmg).";
}
}
\ No newline at end of file
public class Destroyer extends Ship{
// Constant values
public static final int max_hp = 5;
public static final int max_shields = 5;
public static final int damage = 4;
public Destroyer(String name){
// Call the parent's constructor with the proper parameters
super(name,max_hp,max_shields);
}
@Override
protected Attack pick_attack(Ship[] enemies){
// Pick enemy with most hit points
int max_points = -1;
int target_i = -1;
for(int i=0;i<enemies.length;i++){
int points = enemies[i].hp + enemies[i].shields;
if(points > max_points){
max_points = points;
target_i = i;
}
}
if(target_i==-1){
return null;
}else{
return new Attack(this,enemies[target_i],damage);
}
}
@Override
public void update(){
if(!is_destroyed()){
// Recover shield per turn.
this.shields += 1;
if(this.shields > max_shields) this.shields += 1;
}
}
}
\ No newline at end of file
run:
javac *.java
java Spacewar
\ No newline at end of file
public class Scout extends Ship{
// Constant values
public static final int max_hp = 3;
public static final int max_shields = 2;
public static final int damage = 2;
public Scout(String name){
// Call the parent's constructor with the proper parameters
super(name,max_hp,max_shields);
}
@Override
protected Attack pick_attack(Ship[] enemies){
// Pick attack at random
if(enemies.length>0){
final int tgt_i = (int)(Math.random()*enemies.length);
return new Attack(this,enemies[tgt_i],damage);
}else{
return null;
}
}
}
\ No newline at end of file
public abstract class Ship{
public String name;
public int hp;
public int shields;
public Ship(String name, int hp, int shields){
// Set name and alive status
this.name = name;
this.hp = hp;
this.shields = shields;
}
// Wheter the ship is destroyer or not.
public boolean is_destroyed(){
return this.hp<=0;
}
public void receive_damage(int dmg){
if(dmg<=this.shields){
// Just perform shield damage
this.shields -= dmg;
}else{
// Perform shield and hp damage
dmg -= this.shields;
this.shields = 0;
this.hp -= dmg;
// Minimum hp is 0
if(this.hp<0) this.hp = 0;
}
}
// Because the class is abstract, pick_attack doens't need to have a default implementation.
// This method is expected to be reimplemented by all children classes.
protected abstract Attack pick_attack(Ship[] enemies);
// Access to the pick_attack function, ensures that a destroyed ship cannot attack.
public Attack get_attack(Ship[] enemies){
if(is_destroyed()){
return null;
}else{
Attack atk = pick_attack(enemies);
return atk;
}
}
// Update does nothing by default.
// A future version may recieve alliend and enemy as arguments.
public void update(){}
// Displays the ship as a String
@Override
public String toString(){
if(is_destroyed()){
return name+" #destroyed#";
}else{
return name+" ["+hp+"] "+"("+shields+")";
}
}
}
\ No newline at end of file
import java.util.concurrent.TimeUnit;
public class Spacewar {
public static void main(String args[]){
// Initial ships
Ship[] team1 = new Ship[3];
team1[0] = new Destroyer("Empire Des1");
team1[1] = new Scout("Empire Sco1");
team1[2] = new Scout("Empire Sco2");
Ship[] team2 = new Ship[4];
team2[0] = new Scout("Revel Sco1");
team2[1] = new Scout("Revel Sco2");
team2[2] = new Scout("Revel Sco3");
team2[3] = new Scout("Revel Sco4");
// Run simulation
run_simulation(team1,team2);
}
// Create an array with just the ships that are not destroyed
// This would have been more elegant with vectors.
public static Ship[] filter_alive(Ship[] ships){
// Count alive ships
int count_alive = 0;
for(int i=0;i<ships.length;i++){
if(!ships[i].is_destroyed()) count_alive += 1;
}
// Create new array with just alive ships
Ship alive_ships[] = new Ship[count_alive];
count_alive = 0;
for(int i=0;i<ships.length;i++){
if(!ships[i].is_destroyed()){
alive_ships[count_alive++] = ships[i];
}
}
return alive_ships;
}
public static void run_simulation(Ship[] team1, Ship[] team2){
while(true){
Ship[] team1_alive = filter_alive(team1);
Ship[] team2_alive = filter_alive(team2);
// Perform updates
for(int i=0;i<team1.length;i++) team1[i].update();
for(int i=0;i<team2.length;i++) team2[i].update();
// Wait some time
try { TimeUnit.MILLISECONDS.sleep(1000);
} catch(Exception e){}
// Print ships
System.out.println("== Team 1 ==");
for(int i=0;i<team1.length;i++) System.out.println(team1[i]);
System.out.println("== Team 2 ==");
for(int i=0;i<team2.length;i++) System.out.println(team2[i]);
// Check if the simulation terminated
if(team1_alive.length==0 || team2_alive.length==0){
System.out.println("== Terminated. ==");
break;
}
// Wait some time
try { TimeUnit.MILLISECONDS.sleep(2000);
} catch(Exception e){}
// Get all the attacks
int n_attacks = 0;
Attack[] attacks = new Attack[team1.length+team2.length];
for(int i=0;i<team1.length;i++){
attacks[n_attacks++] = team1[i].get_attack(team2_alive);
}
for(int i=0;i<team2.length;i++){
attacks[n_attacks++] = team2[i].get_attack(team1_alive);
}
// Print and execute the attacks
System.out.println("== Attacks: ==");
for(int i=0;i<n_attacks;i++){
if(attacks[i]!=null){
System.out.println(attacks[i]);
attacks[i].execute();
// Wait some time
try { TimeUnit.MILLISECONDS.sleep(400);
} catch(Exception e){}
}
}
System.out.println();
System.out.println();
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment