You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
7.8 KiB

#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;
jsmntok_t t[10000]; /* We expect no more than this count of tokens (jsmn limitation)*/
jsmn_init(&p);
r = jsmn_parse(&p, input_str, strlen(input_str), t,
sizeof(t) / sizeof(t[0]));
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)
{
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;
}