[gnome-robots] introduce ArenaChange struct to represent a turn in a game
- From: Andrey Kutejko <akutejko src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-robots] introduce ArenaChange struct to represent a turn in a game
- Date: Tue, 6 Oct 2020 19:31:41 +0000 (UTC)
commit 47873e0037e39449cadb556a5868a9565fe121f2
Author: Andrey Kutejko <andy128k gmail com>
Date: Sun Sep 20 20:25:38 2020 +0200
introduce ArenaChange struct to represent a turn in a game
src/arena.vala | 12 ++
src/game.vala | 435 +++++++++++++++++++++++++--------------------------------
2 files changed, 205 insertions(+), 242 deletions(-)
---
diff --git a/src/arena.vala b/src/arena.vala
index 79301bf..a6c3f96 100644
--- a/src/arena.vala
+++ b/src/arena.vala
@@ -86,5 +86,17 @@ public class Arena {
return new_arena;
}
+
+ public int count (Gee.Predicate<ObjectType> predicate) {
+ int result = 0;
+ for (int i = 0; i < _width; ++i) {
+ for (int j = 0; j < _height; ++j) {
+ if (predicate (this.arena[i + j * _width])) {
+ result += 1;
+ }
+ }
+ }
+ return result;
+ }
}
diff --git a/src/game.vala b/src/game.vala
index 1247935..b0c917f 100644
--- a/src/game.vala
+++ b/src/game.vala
@@ -72,18 +72,19 @@ public class Game {
public Arena.Coords player { get; private set; }
public Arena.Coords? splat { get; private set; }
- int num_robots1 = 0;
- int num_robots2 = 0;
int endlev_counter = 0;
int current_level = 0;
int score = 0;
int kills = 0;
int score_step = 0;
int safe_teleports = 0;
- int push_xpos = -1;
- int push_ypos = -1;
uint game_timer_id = -1;
- Arena temp_arena = null;
+
+ struct ArenaChange {
+ Arena arena;
+ Arena.Coords player;
+ Arena.Coords? push;
+ }
public Game () {
arena = new Arena (GAME_WIDTH, GAME_HEIGHT);
@@ -188,29 +189,6 @@ public class Game {
update_game_status (score, current_level + 1, safe_teleports);
}
- /**
- * clears the arena
- **/
- void clear_arena () {
- arena.clear ();
- num_robots1 = 0;
- num_robots2 = 0;
- }
-
- /**
- * Set up the temporary arena for processing speculative moves.
- *
- **/
- void load_temp_arena () {
- temp_arena = arena.map ((obj) => {
- if (obj != ObjectType.PLAYER) {
- return obj;
- } else {
- return ObjectType.NONE;
- }
- });
- }
-
private int max_robots () {
return arena.width * arena.height / 2;
}
@@ -222,15 +200,15 @@ public class Game {
* Creates a new level and populates it with robots
**/
void generate_level () {
- clear_arena ();
+ arena.clear ();
player = Arena.Coords () {
x = arena.width / 2,
y = arena.height / 2
};
- arena.@set(player.x, player.y, ObjectType.PLAYER);
+ arena[player.x, player.y] = ObjectType.PLAYER;
- num_robots1 = config.initial_type1 + config.increment_type1 * current_level;
+ var num_robots1 = config.initial_type1 + config.increment_type1 * current_level;
if (num_robots1 > config.maximum_type1) {
num_robots1 = config.maximum_type1;
@@ -242,7 +220,7 @@ public class Game {
play_sound (Sound.VICTORY);
}
- num_robots2 = config.initial_type2 + config.increment_type2 * current_level;
+ var num_robots2 = config.initial_type2 + config.increment_type2 * current_level;
if (num_robots2 > config.maximum_type2) {
num_robots2 = config.maximum_type2;
@@ -277,51 +255,34 @@ public class Game {
}
}
- /**
- * update_arena
- *
- * Description:
- * Copies the temporary arena into the main game arena
- **/
- void update_arena () {
- num_robots1 = 0;
- num_robots2 = 0;
-
- for (int i = 0; i < GAME_WIDTH; ++i) {
- for (int j = 0; j < GAME_HEIGHT; ++j) {
- if (temp_arena[i, j] == ObjectType.HEAP &&
- push_xpos == i && push_ypos == j
- ) {
- if (arena[i, j] == ObjectType.ROBOT1) {
- splat = Arena.Coords () { x = i, y = j };
- play_sound (Sound.SPLAT);
- push_xpos = push_ypos = -1;
- score += config.score_type1_splatted;
- }
- if (arena[i, j] == ObjectType.ROBOT2) {
- splat = Arena.Coords () { x = i, y = j };
- play_sound (Sound.SPLAT);
- push_xpos = push_ypos = -1;
- score += config.score_type2_splatted;
- }
- }
-
- arena[i, j] = temp_arena[i, j];
- if (arena[i, j] == ObjectType.ROBOT1) {
- num_robots1 += 1;
- } else if (arena[i, j] == ObjectType.ROBOT2) {
- num_robots2 += 1;
- }
+ void update_arena (ArenaChange change) {
+ if (change.push != null) {
+ switch (arena[change.push.x, change.push.y]) {
+ case ObjectType.ROBOT1:
+ splat = change.push;
+ play_sound (Sound.SPLAT);
+ score += config.score_type1_splatted;
+ break;
+ case ObjectType.ROBOT2:
+ splat = change.push;
+ play_sound (Sound.SPLAT);
+ score += config.score_type2_splatted;
+ break;
+ default:
+ break;
}
}
+ this.player = change.player;
+ this.arena = change.arena;
+
if (arena[player.x, player.y] != ObjectType.PLAYER) {
kill_player ();
} else {
/* This is in the else statement to catch the case where the last
* two robots collide on top of the human. Without the "else" this
* leads to the player being ressurected and winning. */
- if ((num_robots1 + num_robots2) <= 0) {
+ if (arena.count (obj => obj == ObjectType.ROBOT1 || obj == ObjectType.ROBOT2) <= 0) {
state = State.COMPLETE;
play_sound (Sound.YAHOO);
endlev_counter = 0;
@@ -346,8 +307,13 @@ public class Game {
game_area.queue_draw ();
if ((state == State.TYPE2) || (state == State.WTYPE2)) {
- move_type2_robots ();
- update_arena ();
+ var new_arena = move_type2_robots ();
+ var change = ArenaChange () {
+ arena = new_arena,
+ player = this.player,
+ push = null
+ };
+ update_arena (change);
if (state == State.TYPE2) {
state = State.PLAYING;
} else if (state == State.WTYPE2) {
@@ -381,7 +347,6 @@ public class Game {
return true;
}
-
/**
* Destroys the game timer
**/
@@ -443,40 +408,35 @@ public class Game {
}
/**
- * move_all_robots
- *
- * Description:
* Moves all of the robots and checks for collisions
**/
- void move_all_robots () {
- temp_arena = chase (arena,
- obj => obj == ObjectType.ROBOT1
- || obj == ObjectType.ROBOT2,
- player.x,
- player.y,
- victim => add_kill (victim));
+ private Arena move_all_robots () {
+ return chase (arena,
+ obj => obj == ObjectType.ROBOT1
+ || obj == ObjectType.ROBOT2,
+ player.x,
+ player.y,
+ victim => add_kill (victim));
}
/**
- * move_type2_robots
- *
- * Description:
* Makes the extra move for all of the type2 robots
**/
- void move_type2_robots () {
- temp_arena = chase (arena,
- obj => obj == ObjectType.ROBOT2,
- player.x,
- player.y,
- victim => add_kill (victim));
+ private Arena move_type2_robots () {
+ return chase (arena,
+ obj => obj == ObjectType.ROBOT2,
+ player.x,
+ player.y,
+ victim => add_kill (victim));
}
/**
* Starts the process of moving robots
**/
public void move_robots () {
- move_all_robots ();
+ var new_arena = move_all_robots ();
+ var num_robots2 = arena.count (obj => obj == ObjectType.ROBOT2);
if (num_robots2 > 0) {
if (state == State.WAITING) {
state = State.WTYPE2;
@@ -485,14 +445,20 @@ public class Game {
}
}
- update_arena ();
+ var change = ArenaChange () {
+ arena = new_arena,
+ player = this.player,
+ push = null
+ };
+
+ update_arena (change);
}
delegate void KillTracker(ObjectType victim);
private static Arena chase (Arena arena, Gee.Predicate<ObjectType> is_chaser, int x, int y, KillTracker?
track_kill) {
var new_arena = arena.map ((obj) => {
- if (is_chaser (obj)) {
+ if (obj == ObjectType.PLAYER || is_chaser (obj)) {
return ObjectType.NONE;
} else {
return obj;
@@ -534,109 +500,109 @@ public class Game {
}
}
+ if (new_arena[x, y] == ObjectType.NONE) {
+ new_arena[x, y] = ObjectType.PLAYER;
+ }
+
return new_arena;
}
/**
- * check_safe
- * @x: x position
- * @y: y position
- *
- * Description:
* checks whether a given location is safe
- *
- * Returns:
- * TRUE if location is safe, FALSE otherwise
**/
- bool check_safe (Arena temp_arena, int x, int y) {
- if (temp_arena[x, y] != ObjectType.NONE) {
+ private bool check_safe (ArenaChange change) {
+ var temp2_arena = chase (change.arena,
+ obj => obj == ObjectType.ROBOT1
+ || obj == ObjectType.ROBOT2,
+ change.player.x,
+ change.player.y,
+ null);
+
+ if (temp2_arena[change.player.x, change.player.y] != ObjectType.PLAYER) {
return false;
}
- var temp2_arena = chase (temp_arena, obj => obj == ObjectType.ROBOT1 || obj == ObjectType.ROBOT2, x,
y, null);
+ var temp3_arena = chase (temp2_arena,
+ obj => obj == ObjectType.ROBOT2,
+ change.player.x,
+ change.player.y,
+ null);
- if (temp2_arena[x, y] != ObjectType.NONE) {
+ if (temp3_arena[change.player.x, change.player.y] != ObjectType.PLAYER) {
return false;
}
- var temp3_arena = chase (temp2_arena, (obj) => obj == ObjectType.ROBOT2, x, y, null);
+ return true;
+ }
- if (temp3_arena[x, y] != ObjectType.NONE) {
+ private bool can_push_heap (Arena arena, int x, int y, int dx, int dy) {
+ if (arena[x, y] != ObjectType.HEAP) {
return false;
}
- return true;
- }
-
- /**
- * push_heap
- * @x: x position
- * @y: y position
- * @dx: x direction
- * @dy: y direction
- *
- * Description:
- * pushes a heap in a given direction
- *
- * Returns:
- * TRUE if heap can be pushed, FALSE otherwise
- **/
- bool push_heap (int x, int y, int dx, int dy) {
int nx = x + dx;
int ny = y + dy;
- if (temp_arena.@get (x, y) != ObjectType.HEAP)
- return false;
-
if (nx < 0 || nx >= arena.width || ny < 0 || ny >= arena.height) {
return false;
}
- if (temp_arena.@get (nx, ny) == ObjectType.HEAP)
- return false;
-
- push_xpos = nx;
- push_ypos = ny;
-
- temp_arena.@set (nx, ny, ObjectType.HEAP);
- temp_arena.@set (x, y, ObjectType.NONE);
-
- return true;
+ return arena[nx, ny] != ObjectType.HEAP;
}
/**
- * try_player_move
- * @dx: x direction
- * @dy: y direction
- *
- * Description:
* tries to move the player in a given direction
- *
- * Returns:
- * TRUE if the player can move, FALSE otherwise
**/
- bool try_player_move (int dx, int dy) {
+ private ArenaChange? try_player_move (int dx, int dy) {
int nx = player.x + dx;
int ny = player.y + dy;
- if ((nx < 0) || (nx >= arena.width) || (ny < 0) || (ny >= arena.height)) {
- return false;
+ if (nx < 0 || nx >= arena.width || ny < 0 || ny >= arena.height) {
+ return null;
}
- load_temp_arena ();
-
if (arena[nx, ny] == ObjectType.HEAP) {
- if (config.moveable_heaps) {
- if (!push_heap (nx, ny, dx, dy)) {
- push_xpos = push_ypos = -1;
- return false;
+ // try to push a heap
+ if (!config.moveable_heaps) {
+ return null;
+ }
+ if (!can_push_heap (arena, nx, ny, dx, dy)) {
+ return null;
+ }
+
+ var push = Arena.Coords () { x = nx + dx, y = ny + dy };
+ var new_arena = arena.map ((obj) => {
+ if (obj != ObjectType.PLAYER) {
+ return obj;
+ } else {
+ return ObjectType.NONE;
}
- } else {
- return false;
+ });
+ new_arena[push.x, push.y] = ObjectType.HEAP;
+ new_arena[nx, ny] = ObjectType.PLAYER;
+
+ return ArenaChange () {
+ arena = new_arena,
+ player = Arena.Coords () { x = nx, y = ny },
+ push = push
+ };
+ } else {
+ var new_arena = arena.map ((obj) => {
+ if (obj != ObjectType.PLAYER) {
+ return obj;
+ } else {
+ return ObjectType.NONE;
+ }
+ });
+ if (new_arena[nx, ny] == ObjectType.NONE) {
+ new_arena[nx, ny] = ObjectType.PLAYER;
}
+ return ArenaChange () {
+ arena = new_arena,
+ player = Arena.Coords () { x = nx, y = ny },
+ push = null
+ };
}
-
- return true;
}
/**
@@ -649,49 +615,14 @@ public class Game {
* TRUE if there is a possible safe move, FALSE otherwise
**/
bool safe_move_available () {
- if (try_player_move (-1, -1)) {
- if (check_safe (temp_arena, player.x - 1, player.y - 1)) {
- return true;
- }
- }
- if (try_player_move (0, -1)) {
- if (check_safe (temp_arena, player.x, player.y - 1)) {
- return true;
- }
- }
- if (try_player_move (1, -1)) {
- if (check_safe (temp_arena, player.x + 1, player.y - 1)) {
- return true;
- }
- }
- if (try_player_move (-1, 0)) {
- if (check_safe (temp_arena, player.x - 1, player.y)) {
- return true;
- }
- }
- if (try_player_move (0, 0)) {
- if (check_safe (temp_arena, player.x, player.y)) {
- return true;
- }
- }
- if (try_player_move (1, 0)) {
- if (check_safe (temp_arena, player.x + 1, player.y)) {
- return true;
- }
- }
- if (try_player_move (-1, 1)) {
- if (check_safe (temp_arena, player.x - 1, player.y + 1)) {
- return true;
- }
- }
- if (try_player_move (0, 1)) {
- if (check_safe (temp_arena, player.x, player.y + 1)) {
- return true;
- }
- }
- if (try_player_move (1, 1)) {
- if (check_safe (temp_arena, player.x + 1, player.y + 1)) {
- return true;
+ for (int dx = -1; dx <= 1; ++dx) {
+ for (int dy = -1; dy <= 1; ++dy) {
+ var change = try_player_move (dx, dy);
+ if (change != null) {
+ if (check_safe (change)) {
+ return true;
+ }
+ }
}
}
return false;
@@ -708,18 +639,36 @@ public class Game {
*
*/
bool safe_teleport_available () {
- load_temp_arena ();
-
- for (int x = 0; x < GAME_WIDTH; x++) {
- for (int y = 0; y < GAME_HEIGHT; y++) {
- if (check_safe (temp_arena, x, y))
- return true;
+ for (int x = 0; x < arena.width; x++) {
+ for (int y = 0; y < arena.height; y++) {
+ if (arena[x, y] == ObjectType.NONE) {
+ var change = teleport_to (x, y);
+ if (check_safe (change)) {
+ return true;
+ }
+ }
}
}
return false;
}
+ private ArenaChange teleport_to (int x, int y) {
+ var new_arena = arena.map ((obj) => {
+ if (obj != ObjectType.PLAYER) {
+ return obj;
+ } else {
+ return ObjectType.NONE;
+ }
+ });
+ new_arena[x, y] = ObjectType.PLAYER;
+ return ArenaChange () {
+ arena = new_arena,
+ player = Arena.Coords () { x = x, y = y },
+ push = null
+ };
+ }
+
/**
* player_move
* @dx: x direction
@@ -732,47 +681,29 @@ public class Game {
* TRUE if the player can move, FALSE otherwise
**/
public bool player_move (int dx, int dy) {
+ var change = try_player_move (dx, dy);
- int nx = player.x + dx;
- int ny = player.y + dy;
+ if (change == null) {
+ play_sound (Sound.BAD);
+ return false;
+ }
if (properties_safe_moves ()) {
- if (!try_player_move (dx, dy)) {
- play_sound (Sound.BAD);
- return false;
- } else {
- if (!check_safe (temp_arena, nx, ny)) {
- if (properties_super_safe_moves () || safe_move_available ()) {
- play_sound (Sound.BAD);
- return false;
- }
+ if (!check_safe (change)) {
+ if (properties_super_safe_moves () || safe_move_available ()) {
+ play_sound (Sound.BAD);
+ return false;
}
}
- } else {
- if (!try_player_move (dx, dy)) {
- play_sound (Sound.BAD);
- return false;
- }
- }
-
- player = Arena.Coords () {
- x = nx,
- y = ny
- };
-
- if (temp_arena.@get (player.x, player.y) == ObjectType.NONE) {
- temp_arena.@set (player.x, player.y, ObjectType.PLAYER);
}
splat = null;
+ update_arena (change);
game_area.queue_draw ();
- update_arena ();
-
return true;
}
-
/**
* random_teleport
*
@@ -783,7 +714,7 @@ public class Game {
* TRUE if the player can be teleported, FALSE otherwise
**/
bool random_teleport () {
- temp_arena = arena.map ((obj) => {
+ var temp_arena = arena.map ((obj) => {
if (obj != ObjectType.PLAYER) {
return obj;
} else {
@@ -794,9 +725,14 @@ public class Game {
var p = random_position ((x, y) => temp_arena[x, y] == ObjectType.NONE);
if (p != null) {
temp_arena[p.x, p.y] = ObjectType.PLAYER;
- player = p;
- update_arena ();
+ var change = ArenaChange () {
+ arena = temp_arena,
+ player = p,
+ push = null
+ };
+
+ update_arena (change);
splat = null;
game_area.queue_draw ();
play_sound (Sound.TELEPORT);
@@ -829,7 +765,7 @@ public class Game {
if (safe_teleports <= 0)
return false;
- temp_arena = arena.map ((obj) => {
+ var temp_arena = arena.map ((obj) => {
if (obj != ObjectType.PLAYER) {
return obj;
} else {
@@ -837,15 +773,30 @@ public class Game {
}
});
- var p = random_position ((x, y) => temp_arena[x, y] == ObjectType.NONE && check_safe (temp_arena, x,
y));
+ var p = random_position ((x, y) => {
+ if (temp_arena[x, y] != ObjectType.NONE) {
+ return false;
+ }
+ var try_change = ArenaChange () {
+ arena = temp_arena,
+ player = Arena.Coords() { x = x, y = y },
+ push = null
+ };
+ return check_safe (try_change);
+ });
if (p != null) {
temp_arena[p.x, p.y] = ObjectType.PLAYER;
- player = p;
safe_teleports -= 1;
update_game_status (score, current_level, safe_teleports);
- update_arena ();
+ var change = ArenaChange () {
+ arena = temp_arena,
+ player = p,
+ push = null
+ };
+
+ update_arena (change);
splat = null;
game_area.queue_draw ();
play_sound (Sound.TELEPORT);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]