|
|
|
#include "jsmn.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <queue>
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
// Parsování JSONu
|
|
|
|
|
|
|
|
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
|
|
|
|
if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
|
|
|
|
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Třídy reprezentující hru
|
|
|
|
|
|
|
|
enum Direction
|
|
|
|
{
|
|
|
|
STAY = -1,
|
|
|
|
UP = 0,
|
|
|
|
LEFT = 1,
|
|
|
|
DOWN = 2,
|
|
|
|
RIGHT = 3,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Team;
|
|
|
|
struct Member;
|
|
|
|
struct Field;
|
|
|
|
|
|
|
|
struct Team
|
|
|
|
{
|
|
|
|
int id;
|
|
|
|
bool is_me;
|
|
|
|
Field* home;
|
|
|
|
vector<Field*> occupied;
|
|
|
|
vector<Field*> protected_fields;
|
|
|
|
vector<Member*> members;
|
|
|
|
Team(int _id, bool _is_me)
|
|
|
|
{
|
|
|
|
id = id;
|
|
|
|
is_me = _is_me;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Member
|
|
|
|
{
|
|
|
|
Field * field;
|
|
|
|
Team * team;
|
|
|
|
int id;
|
|
|
|
Direction action = STAY;
|
|
|
|
Member(Field *_field)
|
|
|
|
{
|
|
|
|
field = _field;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Field
|
|
|
|
{
|
|
|
|
int i,j;
|
|
|
|
bool hill;
|
|
|
|
Team* home_for_team = NULL;
|
|
|
|
Team* occupied_by_team = NULL;
|
|
|
|
Team* protected_for_team = NULL;
|
|
|
|
vector<Member*> members;
|
|
|
|
Field(int _i, int _j)
|
|
|
|
{
|
|
|
|
i = _i;
|
|
|
|
j = _j;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Celý stav hry včetně parsování a vypisování
|
|
|
|
struct State
|
|
|
|
{
|
|
|
|
// Dealokace paměti není implementována, protože předpokládáme, že program
|
|
|
|
// poběží jen krátce a po ukončení všechna paměť automaticky zanikne.
|
|
|
|
vector<Team*> teams;
|
|
|
|
vector<vector<Field*>> world;
|
|
|
|
Team * my_team;
|
|
|
|
int round_number;
|
|
|
|
int time_to_response;
|
|
|
|
|
|
|
|
int js_skip(jsmntok_t* t, int i)
|
|
|
|
{
|
|
|
|
int s = t[i].size;
|
|
|
|
i++;
|
|
|
|
for(int j=0; j<s; j++)
|
|
|
|
i = js_skip(t, i);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
State(char * input_str)
|
|
|
|
{
|
|
|
|
// Ano, vím, že je to ošklivé, ale prostě parsovat JSON v C/C++ je za trest
|
|
|
|
int r;
|
|
|
|
jsmn_parser p;
|
|
|
|
int t_len = 100000000;
|
|
|
|
jsmntok_t * t = new jsmntok_t[t_len]; /* We expect no more than this count of tokens (jsmn limitation)*/
|
|
|
|
|
|
|
|
jsmn_init(&p);
|
|
|
|
r = jsmn_parse(&p, input_str, strlen(input_str), t, t_len);
|
|
|
|
assert(!(r < 0));
|
|
|
|
|
|
|
|
assert(!(r < 1 || t[0].type != JSMN_OBJECT));
|
|
|
|
|
|
|
|
int teams_count, team_id;
|
|
|
|
int state_tok, world_tok;
|
|
|
|
for (int i = 1, c=0; c < t[0].size;i = js_skip(t, i), c++)
|
|
|
|
{
|
|
|
|
//printf("- %.*s %d\n", t[i].end - t[i].start, input_str + t[i].start, t[i].size);
|
|
|
|
|
|
|
|
if (jsoneq(input_str, &t[i], "teams_count") == 0)
|
|
|
|
teams_count = atoi(input_str + t[i+1].start);
|
|
|
|
if (jsoneq(input_str, &t[i], "team_id") == 0)
|
|
|
|
team_id = atoi(input_str + t[i+1].start);
|
|
|
|
if (jsoneq(input_str, &t[i], "time_to_response") == 0)
|
|
|
|
time_to_response = atoi(input_str + t[i+1].start);
|
|
|
|
if (jsoneq(input_str, &t[i], "round_number") == 0)
|
|
|
|
round_number = atoi(input_str + t[i+1].start);
|
|
|
|
if (jsoneq(input_str, &t[i], "state") == 0)
|
|
|
|
state_tok = i+1;
|
|
|
|
;
|
|
|
|
}
|
|
|
|
assert(state_tok);
|
|
|
|
for(int i=0; i<teams_count; i++)
|
|
|
|
teams.push_back(new Team(i, i==team_id));
|
|
|
|
my_team = teams[team_id];
|
|
|
|
for (int i = state_tok+1, c=0; c < t[state_tok].size;i = js_skip(t, i), c++)
|
|
|
|
{
|
|
|
|
if (jsoneq(input_str, &t[i], "world") == 0)
|
|
|
|
world_tok = i+1;
|
|
|
|
;
|
|
|
|
}
|
|
|
|
assert(world_tok);
|
|
|
|
for (int i = world_tok+1, x=0; x < t[world_tok].size;i = js_skip(t, i), x++)
|
|
|
|
{
|
|
|
|
vector<Field *> current_row;
|
|
|
|
for (int j = i+1, y=0; y < t[i].size;j = js_skip(t, j), y++)
|
|
|
|
{
|
|
|
|
Field *current_field = new Field(x,y);
|
|
|
|
int members_tok;
|
|
|
|
for (int k = j+1, c=0; c < t[j].size;k = js_skip(t, k), c++)
|
|
|
|
{
|
|
|
|
if (jsoneq(input_str, &t[k], "hill") == 0)
|
|
|
|
current_field->hill = input_str[t[k+1].start] == 't';
|
|
|
|
if (jsoneq(input_str, &t[k], "home_for_team") == 0)
|
|
|
|
if(input_str[t[k+1].start] != 'n')
|
|
|
|
{
|
|
|
|
current_field->home_for_team = teams[atoi(input_str+t[k+1].start)];
|
|
|
|
current_field->home_for_team->home = current_field;
|
|
|
|
}
|
|
|
|
if (jsoneq(input_str, &t[k], "occupied_by_team") == 0)
|
|
|
|
if(input_str[t[k+1].start] != 'n')
|
|
|
|
{
|
|
|
|
current_field->occupied_by_team = teams[atoi(input_str+t[k+1].start)];
|
|
|
|
current_field->occupied_by_team->occupied.push_back(current_field);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jsoneq(input_str, &t[k], "protected_for_team") == 0)
|
|
|
|
if(input_str[t[k+1].start] != 'n')
|
|
|
|
{
|
|
|
|
current_field->protected_for_team = teams[atoi(input_str+t[k+1].start)];
|
|
|
|
current_field->protected_for_team->protected_fields.push_back(current_field);
|
|
|
|
}
|
|
|
|
if (jsoneq(input_str, &t[k], "members") == 0)
|
|
|
|
members_tok = k+1;
|
|
|
|
}
|
|
|
|
for (int k = members_tok+1, c=0; c < t[members_tok].size;k = js_skip(t, k), c++)
|
|
|
|
{
|
|
|
|
auto m = new Member(current_field);
|
|
|
|
for (int l = k+1, d=0; d < t[k].size;l = js_skip(t, l), d++)
|
|
|
|
{
|
|
|
|
// printf("%d %d: %.*s %d\n", x,y, t[l].end - t[l].start, input_str + t[l].start, t[l].size);
|
|
|
|
if (jsoneq(input_str, &t[l], "id") == 0)
|
|
|
|
m->id = atoi(input_str + t[l+1].start);
|
|
|
|
if (jsoneq(input_str, &t[l], "team") == 0)
|
|
|
|
m->team = teams[atoi(input_str+t[l+1].start)];
|
|
|
|
}
|
|
|
|
m->team->members.push_back(m);
|
|
|
|
current_field->members.push_back(m);
|
|
|
|
}
|
|
|
|
current_row.push_back(current_field);
|
|
|
|
}
|
|
|
|
world.push_back(current_row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_turn()
|
|
|
|
{
|
|
|
|
for(Team *t: teams)
|
|
|
|
if(!t->is_me)
|
|
|
|
for(Member *m: t->members)
|
|
|
|
if(m->action != STAY)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Voják cizího týmu má přiřazenou akci.");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
bool first = true;
|
|
|
|
printf("{\"members\": [");
|
|
|
|
for(Member *m: my_team->members)
|
|
|
|
{
|
|
|
|
if(!first) printf(", ");
|
|
|
|
const char * action = "stay";
|
|
|
|
if(m->action == UP) action = "up";
|
|
|
|
if(m->action == LEFT) action = "left";
|
|
|
|
if(m->action == DOWN) action = "down";
|
|
|
|
if(m->action == RIGHT) action = "right";
|
|
|
|
printf("{\"id\": %d, \"action\": \"%s\"}", m->id, action);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
printf("]}");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
State* state;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Algoritmy
|
|
|
|
|
|
|
|
// otoč směr podle jiného otočení
|
|
|
|
Direction combine_directions(Direction a, Direction b)
|
|
|
|
{
|
|
|
|
if (a == STAY || b == STAY)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Nelze kombinovat se STAY.");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return (Direction) ((a + b) % 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// získej opačný směr
|
|
|
|
Direction invert_direction(Direction a)
|
|
|
|
{
|
|
|
|
if (a == STAY) return STAY;
|
|
|
|
return combine_directions(a, DOWN);
|
|
|
|
}
|
|
|
|
|
|
|
|
// další políčko v daném směru
|
|
|
|
Field* get_neighbour_field(Field* f, Direction direction)
|
|
|
|
{
|
|
|
|
int neighbour_i, neighbour_j;
|
|
|
|
switch (direction)
|
|
|
|
{
|
|
|
|
case UP:
|
|
|
|
neighbour_i = f->i - 1;
|
|
|
|
neighbour_j = f->j;
|
|
|
|
break;
|
|
|
|
case DOWN:
|
|
|
|
neighbour_i = f->i + 1;
|
|
|
|
neighbour_j = f->j;
|
|
|
|
break;
|
|
|
|
case LEFT:
|
|
|
|
neighbour_i = f->i;
|
|
|
|
neighbour_j = f->j - 1;
|
|
|
|
break;
|
|
|
|
case RIGHT:
|
|
|
|
neighbour_i = f->i;
|
|
|
|
neighbour_j = f->j + 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
neighbour_i = f->i;
|
|
|
|
neighbour_j = f->j;
|
|
|
|
}
|
|
|
|
|
|
|
|
// zajisti, aby souřadnice byly v rozsahu herní plochy
|
|
|
|
neighbour_i %= state->world.size();
|
|
|
|
neighbour_j %= state->world[0].size();
|
|
|
|
return state->world[neighbour_i][neighbour_j];
|
|
|
|
}
|
|
|
|
|
|
|
|
// jestli daný tým může vstoupit na políčko
|
|
|
|
bool is_field_accessible(Field* f, Team* t)
|
|
|
|
{
|
|
|
|
if (f->hill) return false;
|
|
|
|
if (f->occupied_by_team != NULL && f->occupied_by_team != t) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Najdi nejkratší cestu od vojáka k cílovému políčku.
|
|
|
|
// Vrátí první krok, který má voják udělat.
|
|
|
|
// Pokud žádná cesta neexistuje, vrátí STAY.
|
|
|
|
Direction pathfind(Member* m, Field* goal)
|
|
|
|
{
|
|
|
|
// speciální případ: voják už je v cíli
|
|
|
|
if (m->field == goal) return STAY;
|
|
|
|
|
|
|
|
std::set<Field*> explored;
|
|
|
|
std::queue<Field*> queue;
|
|
|
|
queue.push(goal);
|
|
|
|
auto dirs = {UP, LEFT, DOWN, RIGHT};
|
|
|
|
while (!queue.empty())
|
|
|
|
{
|
|
|
|
auto field = queue.front();
|
|
|
|
queue.pop();
|
|
|
|
for (auto direction : dirs)
|
|
|
|
{
|
|
|
|
auto neighbour = get_neighbour_field(field, direction);
|
|
|
|
if (explored.find(neighbour) != explored.end()) continue;
|
|
|
|
if (!is_field_accessible(neighbour, m->team)) continue;
|
|
|
|
// pokud jsme našli vojáka, vrátíme první krok
|
|
|
|
if (neighbour == m->field) return invert_direction(direction);
|
|
|
|
queue.push(neighbour);
|
|
|
|
explored.insert(neighbour);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return STAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Strategie
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
char * input_str;
|
|
|
|
scanf("%m[^\n]", &input_str);
|
|
|
|
state = new State(input_str);
|
|
|
|
|
|
|
|
// TODO: zde doplňte svou herní strategii
|
|
|
|
for(Member *m: state->my_team->members)
|
|
|
|
m->action = UP;
|
|
|
|
|
|
|
|
state->print_turn();
|
|
|
|
return 0;
|
|
|
|
}
|