fg[p].call[i][j] = 0;
if ( (msg->flag[i][j] & (1<fg[p].flag[i][j] = 0;
else
st->fg[p].flag[i][j] = 1;
}
}
}
return 0;
}
int client_receive_msg_s (int sfd, struct state *st) {
static uint8_t buf[MSG_BUF_SIZE];
static struct msg_s_data msg_data;
struct sockaddr_storage peer_addr; /* address you receive a message from */
socklen_t peer_addr_len = sizeof(peer_addr);
int nread = recvfrom(sfd, buf, MSG_BUF_SIZE-1, 0,
(struct sockaddr *) &peer_addr, &peer_addr_len);
if (nread == -1) return -1; /* Ignore failed request */
uint8_t msg = 0;
if (nread >= 1) {
msg = buf[0];
switch (msg) {
case MSG_S_STATE:
if (nread-1 >= sizeof(struct msg_s_data)) {
memcpy(&msg_data, buf+1, sizeof(struct msg_s_data));
client_process_msg_s_state (st, &msg_data);
}
else {return -1;}
break;
}
}
return msg;
}
int client_process_input (struct state *st, struct ui *ui, char c, int sfd, struct addrinfo *srv_addr) {
int cursi = ui->cursor.i;
int cursj = ui->cursor.j;
switch (c) {
case 'Q':
case 'q':
return 1; /* quit program */
/*
case 'f':
st->prev_speed = st->speed;
st->speed = faster(st->speed);
break;
case 's':
st->prev_speed = st->speed;
st->speed = slower(st->speed);
break;
*/
case 'p':
if (st->speed == sp_pause)
send_msg_c (sfd, srv_addr, MSG_C_PAUSE, 0, 0, 0);
else {
send_msg_c (sfd, srv_addr, MSG_C_UNPAUSE, 0, 0, 0);
}
break;
case 'h': case K_LEFT:
cursi--;
break;
case 'l': case K_RIGHT:
cursi++;
break;
case 'k': case K_UP:
cursj--;
if (cursj % 2 == 1)
cursi++;
break;
case 'j': case K_DOWN:
cursj++;
if (cursj % 2 == 0)
cursi--;
break;
case ' ':
if (st->fg[st->controlled].flag[ui->cursor.i][ui->cursor.j] == 0)
send_msg_c (sfd, srv_addr, MSG_C_FLAG_ON, ui->cursor.i, ui->cursor.j, 0);
else
send_msg_c (sfd, srv_addr, MSG_C_FLAG_OFF, ui->cursor.i, ui->cursor.j, 0);
break;
case 'x':
send_msg_c (sfd, srv_addr, MSG_C_FLAG_OFF_ALL, 0, 0, 0);
break;
case 'c':
send_msg_c (sfd, srv_addr, MSG_C_FLAG_OFF_HALF, 0, 0, 0);
break;
case 'r':
case 'v':
send_msg_c (sfd, srv_addr, MSG_C_BUILD, ui->cursor.i, ui->cursor.j, 0);
break;
case ESCAPE:
case 91:
break;
}
cursi = IN_SEGMENT(cursi, 0, st->grid.width-1);
cursj = IN_SEGMENT(cursj, 0, st->grid.height-1);
if ( is_visible(st->grid.tiles[cursi][cursj].cl) ) {
ui->cursor.i = cursi;
ui->cursor.j = cursj;
}
return 0; /* not finished */
}
void send_msg_c (int sfd, struct addrinfo *srv_addr, uint8_t msg, uint8_t i, uint8_t j, uint8_t info) {
struct msg_c_data mcd = {i, j, info};
static uint8_t buf[MSG_BUF_SIZE];
buf[0] = msg;
memcpy(buf+1, &mcd, sizeof(mcd));
int nsent = sendto(sfd, buf, 1+sizeof(mcd), 0, srv_addr->ai_addr, srv_addr->ai_addrlen);
if (nsent == -1) {
perror("client: sendto");
}
}
client.h 0000644 0001750 0001750 00000002466 12170266401 012414 0 ustar sicness sicness /******************************************************************************
Curse of War -- Real Time Strategy Game for Linux.
Copyright (C) 2013 Alexey Nikolaev.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#ifndef _CLIENT_H
#define _CLIENT_H
#include "network.h"
#include "messaging.h"
#include "state.h"
int client_process_input (struct state *st, struct ui *ui, char c, int sfd, struct addrinfo *srv_addr);
int client_process_msg_s_state (struct state *st, struct msg_s_data *msg);
int client_receive_msg_s (int sfd, struct state *st);
void send_msg_c (int sfd, struct addrinfo *srv_addr, uint8_t msg, uint8_t i, uint8_t j, uint8_t info);
#endif
common.h 0000644 0001750 0001750 00000003426 12173210021 012411 0 ustar sicness sicness /******************************************************************************
Curse of War -- Real Time Strategy Game for Linux.
Copyright (C) 2013 Alexey Nikolaev.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#ifndef _COMMON_H
#define _COMMON_H
#define MAX_PLAYER 8 /* number of players (countries) */
#define NEUTRAL 0 /* neutral player */
#define MAX_CLASS 1 /* classes of units. only one exists. */
#define MAX_WIDTH 40 /* max map width */
#define MAX_HEIGHT 29 /* max map height */
#define DIRECTIONS 6 /* number of neighbors on the grid */
#define MAX_POP 499 /* maximum polulation at a tile (for each player) */
#define MAX_TIMELINE_MARK 72
#define MIN(x,y) (x(r))?(r):(x) ))
#define ESCAPE '\033'
#define K_UP 65
#define K_DOWN 66
#define K_RIGHT 67
#define K_LEFT 68
/* game speed */
enum config_speed {sp_pause, sp_slowest, sp_slower, sp_slow, sp_normal, sp_fast, sp_faster, sp_fastest};
/* game difficulty */
enum config_dif {dif_easiest, dif_easy, dif_normal, dif_hard, dif_hardest};
#endif
curseofwar.6 0000644 0001750 0001750 00000015006 12175013122 013221 0 ustar sicness sicness .TH CURSEOFWAR "6" "July 2013" "curseofwar" "v%VERSION%"
.SH NAME
curseofwar \- Real Time Strategy Game for Linux
.SH SYNOPSIS
.B curseofwar
[
.B \-c
.I port
]
[
.B \-C
.I IP
]
[
.B \-d
.I difficulty
]
[
.B \-e
.I port
]
.PD 0
.IP
.PD
[
.B \-E
.I clients
]
[
.B \-h
]
[
.B \-H
.I height
]
[
.B \-i
.I inequality
]
.PD 0
.IP
.PD
[
.B \-l
.I countries
]
[
.B \-q
.I quality
]
[
.B \-r
]
[
.B \-R
.I seed
]
[
.B \-s
.I speed
]
[
.B \-S
.I shape
]
[
.B \-T
]
[
.B \-W
.I width
]
.SH DESCRIPTION
This is a fast-paced action strategy game for Linux implemented using ncurses user interface.
.PP
Unlike most RTS, you are not controlling units, but focus on high-level strategic planning: Building infrastructure, securing resources, and moving your armies.
.PP
The core game mechanics turns out to be quite close to WWI-WWII type of warfare, however, there is no explicit reference to any historical period.
.SH OPTIONS
.TP
\fB\-c\fR \fIport\fR
Clients's port (19150 is default).
.TP
\fB\-C\fR \fIIP\fR
Start a client and connect to the provided server's IP\-address.
.TP
\fB\-d\fR [ee|e|n|h|hh]
Difficulty level (AI) from the easiest to the hardest (default is normal).
.TP
\fB\-e\fR \fIport\fR
Server's port (19140 is default).
.TP
\fB\-E\fR [1|2| ... L]
Start a server for not more than L clients.
.TP
\fB\-h\fR
Display this help
.TP
\fB\-H\fR \fIheight\fR
Map height (default is 21, maximum is 29)
.TP
\fB\-i\fR [0|1|2|3|4]
Inequality between the countries (0 is the lowest, 4 in the highest).
.TP
\fB\-l\fR [2|3| ... N]
Sets L, the number of countries (default is N).
.TP
\fB\-q\fR [1|2| ... L]
Choose player's location by its quality (1 = the best available on the map, L = the worst). Only in the singleplayer mode.
.TP
\fB\-r\fR
Absolutely random initial conditions, overrides options \fB\-l\fR, \fB\-i\fR, and \fB\-q\fR.
.TP
\fB\-R\fR \fIseed\fR
Specify a random seed (unsigned integer) for map generation.
.TP
\fB\-s\fR [p|sss|ss|s|n|f|ff|fff]
Game speed from the slowest to the fastest (default is normal).
.TP
\fB\-S\fR [rhombus|rect|hex]
Map shape (rectangle is default). Max number of countries N=4 for rhombus and rectangle, and N=6 for the hexagon.
.TP
\fB\-T\fR
Show the timeline.
.TP
\fB\-W\fR \fIwidth\fR
Map width (default is 21, maximum is 40)
.SH "HOW TO PLAY"
Normally, the game starts with 4 small countries in the corners of the map.
You start as the ruler of the Green country, and your goal is to conquer
the whole map.
.B Tiles
The map is made of hexagonal tiles. They are:
/\\^ Mountains, cannot be populated
/$\\ Gold mines, cannot be populated too.
This is the source of gold for your country.
To control a mine, surround it with your army
n Villages, have the slowest population growth (+10%)
i=i Towns, average population growth (+20%)
W#W Fortresses, high population growth (+30%)
- Grassland, normal habitable territory
.B People
People are your primary resource. Thriving popluation is essential for your
victory.
Every tile can support at most 499 people from each country.
When people from more than one country occupy the same tile, they fight.
The country that has the highest population at the tile is called the tile
owner.
The population of the tile owner is shown on all grassland tiles as follows:
\. 1 - 3 citizens
.br
\&.. 4 - 6
.br
\&... 7 - 12
.br
: 13 - 25
.br
\&.: 26 - 50
.br
\&.:. 51 - 100
.br
:: 101 - 200
.br
\&.:: 201 - 400
.br
::: 400 - 499
People migrate from highly populated tiles to less populated tiles.
Population grows only in cities. In villages by 10%, in towns by 20%, and
fortresses by 30% every simulation step. By controlling cities, you control
their surrounding territory.
.B Flags
Every country can put flags on the map. Flags change migration rates,
so that people of your country start gathering at the tiles with flags.
Player's flags are shown as white "P" on the right side of a tile.
Flags of the computer opponents are shown as "x" on the left side of a tile.
The flags can be used to increase population of your own cities, as well as
for conquering foreign territories.
When countries are fighting for a city, and if the damage to the defender's
army is significant, the city can be destroyed: A fortress becomes a town,
a town becomes a village, and the village gets completely burnt by the invaders.
.TP
.B Countries
.TP
Computer opponents differ in personality, and it affects the way they fight.
.SH CONTROLS
Arrow keys and H, J, K, L are for moving the cursor
R or V build village -> town -> fortress
Space add/remove a flag
.br
X remove all your flags
.br
C remove a half of your flags
S slower
.br
F faster
Q quit
.SH MULTIPLAYER
To start a server for two players:
.IP
.B curseofwar
\-E 2
.PP
To start a client and connect to the server:
.IP
.B curseofwar
\-C
.PP
To specify ports, use \-e option for server's port, and \-c option for
client's port. By default, servers are using port 19140, and clients are
using port 19150.
.B Examples:
Start a server for a single client using port 11111
.IP
.B curseofwar
\-E 1 \-e 11111
.PP
To connect to it:
.IP
.B curseofwar
\-C \-e 11111
.PP
Alternatively, to connect to it using port 12345 on the client's side:
.IP
.B curseofwar
\-C \-c 12345 \-e 11111
.PP
Note that all needed map options must be setup when you start a server,
the map and other data are transmitted to clients, once they are connected.
.B Example:
Server for 3 clients, no computer opponents, hexagonal map, and equal
conditions for all:
.IP
.B curseofwar
\-E3 \-l3 \-S hex \-i0
.PP
Game speed cannot be changed by a client, so it must be set initially by
the server. Not all data is sent to clients (e.g. info about population is
not sent in full).
Multiplayer mode is at relatively early development stage. Changes may occure
at any moment. When you play with other people, make sure that you are using
the same version of the game. Hopefully, game's client-server communication
protocol will be improved in future. All communication is made via UDP.
Please, report you problems with multiplayer.
.SH EXAMPLES
A good and easy mode to start playing:
.IP
.B curseofwar
\-i4 \-q1 \-dee
.PP
Or, on a smaller map:
.IP
.B curseofwar
\-i4 \-q1 \-dee \-W16 \-H16
.SH AUTHORS
.B Game:
.br
Alexey Nikolaev
.br
.B Makefile:
.br
Kirill Dmitrenko
.br
Maximilian Dietrich
.br
.B Manpage:
.br
Anton Balashov
.br
Maximilian Dietrich
curseofwar.menu 0000644 0001750 0001750 00000000454 12173133004 014021 0 ustar sicness sicness ?package(curseofwar):\
needs="text" \
command="/usr/bin/curseofwar" \
hints="Strategy,Fast-paced,Action,Terminal,ncurses" \
longtitle="A fast-paced action strategy game" \
section="Games/Strategy" \
title="Curse of War (console)" \
icon="/usr/share/pixmaps/curseofwar-32x32.xpm"
grid.c 0000644 0001750 0001750 00000035723 12170266401 012060 0 ustar sicness sicness /******************************************************************************
Curse of War -- Real Time Strategy Game for Linux.
Copyright (C) 2013 Alexey Nikolaev.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#include "grid.h"
int is_a_city(enum tile_class t) {
switch(t) {
case village: return 1;
case town: return 1;
case castle: return 1;
default: return 0;
}
}
int is_inhabitable(enum tile_class t) {
switch(t) {
case abyss:
case mountain:
case mine: return 0;
default: return 1;
}
}
int is_visible(enum tile_class t) {
switch(t) {
case abyss: return 0;
default: return 1;
}
}
const struct loc dirs[DIRECTIONS] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {1,-1}, {-1, 1}};
void grid_init(struct grid *g, int w, int h){
g->width = MIN(w, MAX_WIDTH);
g->height = MIN(h, MAX_HEIGHT);
int i, j;
for(i=0; iwidth; ++i) {
for(j=0; jheight; ++j) {
g->tiles[i][j].cl = grassland;
int x = rand() % 20;
if(0 == x) {
int y = rand() % 6;
switch (y){
case 0:
g->tiles[i][j].cl = castle; break;
case 1: case 2:
g->tiles[i][j].cl = town; break;
default:
g->tiles[i][j].cl = village;
}
}
if(x > 0 && x < 5) {
// mountains and mineis
if (rand() % 10 == 0)
g->tiles[i][j].cl = mine;
else
g->tiles[i][j].cl = mountain;
g->tiles[i][j].pl = NEUTRAL;
}
else {
x = 1 + rand() % (MAX_PLAYER - 1);
if (x < MAX_PLAYER) g->tiles[i][j].pl = x;
else
g->tiles[i][j].pl = NEUTRAL;
}
int p, c;
for (p=0; ptiles[i][j].units[p][c] = 0;
}
}
if (is_a_city(g->tiles[i][j].cl)) {
int owner = g->tiles[i][j].pl;
g->tiles[i][j].units[owner][citizen] = 10;
}
}
}
}
/* Stencils */
int stencil_avlbl_loc_num (enum stencil st){
switch(st) {
case st_rhombus: return 4;
case st_rect: return 4;
case st_hex: return 6;
}
return 0;
}
#define X_OF_IJ(i,j) 0.5*(j) + (i)
#define Y_OF_IJ(i,j) (float)(j)
void stencil_rhombus (struct grid *g, int d, struct loc loc[MAX_AVLBL_LOC]) {
int xs[] = {d, g->width-1-d, d, g->width-1-d};
int ys[] = {d, g->height-1-d, g->height-1-d, d};
int loc_num = 4;
int k;
for(k=0; kheight-1) - epsilon;
float y0 = Y_OF_IJ(0, 0) - epsilon;
float x1 = X_OF_IJ(g->width-1, 0) + epsilon;
float y1 = Y_OF_IJ(0, g->height-1) + epsilon;
for (i=0; iwidth; ++i)
for (j=0; jheight; ++j) {
x = X_OF_IJ(i,j);
y = Y_OF_IJ(i,j);
if (xx1 || yy1)
g->tiles[i][j].cl = abyss;
}
int loc_num = 4;
int dx = g->height/2;
struct loc temp_loc[] = {
{dx+d-1, d},
{g->width-dx-1-d+1, g->height-1-d},
{d+1, g->height-1-d},
{g->width-1-d-1, d}
};
int k;
for(k=0; kheight/2;
for (i=0; iwidth; ++i)
for (j=0; jheight; ++j) {
if (i+jg->width-1+g->height-1-dx)
g->tiles[i][j].cl = abyss;
}
int loc_num = 6;
struct loc temp_loc[] = {
{dx+d-2, d}, // tl
{d, g->height-1-d}, // bl
{g->width-1-d, dx}, // cr
{d, dx}, // cl
{g->width-1-d-2+2, d}, // tr
{g->width-1-dx-d+2, g->height-1-d} // br
};
int k;
for(k=0; kwidth; ++i)
for (j=0; jheight; ++j) {
if (g->tiles[i][j].cl == abyss) {
int p;
for(p=0; ptiles[i][j].units[p][citizen] = 0;
g->tiles[i][j].pl = NEUTRAL;
}
}
}
}
/* Ended Stencils */
/* helper.
* floodfill with value val, the closest distance has priority */
void floodfill_closest (struct grid *g, int u[MAX_WIDTH][MAX_HEIGHT], int d[MAX_WIDTH][MAX_HEIGHT], int x, int y, int val, int dist) {
if (x < 0 || x >= g->width || y < 0 || y >= g->height || is_inhabitable(g->tiles[x][y].cl) == 0 || d[x][y] <= dist) {
return;
}
u[x][y] = val;
d[x][y] = dist;
int k;
for (k = 0; kwidth; ++i)
for(j=0; jheight; ++j) {
d[i][j] = MAX_WIDTH * MAX_HEIGHT + 1;
u[i][j] = unreachable;
}
int k;
for(k=0; kwidth; ++i)
for(j=0; jheight; ++j){
if (is_inhabitable (g->tiles[i][j].cl)) {
g->tiles[i][j].units[u[i][j]][citizen] = 1;
g->tiles[i][j].pl = u[i][j];
}
}
*/
for(i=0; iwidth; ++i)
for(j=0; jheight; ++j){
if (g->tiles[i][j].cl == mine) {
int single_owner = unreachable;
int max_dist = 0;
int min_dist = MAX_WIDTH * MAX_HEIGHT + 1;
for (k = 0; k= g->width || y < 0 || y >= g->height || is_inhabitable(g->tiles[x][y].cl) == 0) {
continue;
}
//g->tiles[x][y].units[u[x][y]][citizen] = 401;
//g->tiles[x][y].pl = u[x][y];
if (single_owner == unreachable) {
single_owner = u[x][y];
max_dist = d[x][y];
min_dist = d[x][y];
}
else {
if (u[x][y] == single_owner) {
max_dist = MAX(max_dist, d[x][y]);
min_dist = MIN(min_dist, d[x][y]);
}
else if (u[x][y] != unreachable) single_owner = competition;
}
}
if (single_owner != competition && single_owner != unreachable)
result[single_owner] += (int) ( 100.0 * (MAX_WIDTH + MAX_HEIGHT) * exp(-10.0 * (float)max_dist*min_dist / (MAX_WIDTH*MAX_HEIGHT)) );
}
}
return;
}
/* simple shuffling of an array of integers */
void shuffle (int arr[], int len) {
int t, i, j, s;
for(t=0; twidth; ++i) {
for(j=0; jheight; ++j) {
for (p=0; p< MAX_PLAYER; ++p) {
for (c = 0; ctiles[i][j].units[p][c] = 0;
g->tiles[i][j].pl = NEUTRAL;
if (is_a_city(g->tiles[i][j].cl))
g->tiles[i][j].cl = grassland;
//g->tiles[i][j].pl = NEUTRAL;
}
}
}
}
locations_num = IN_SEGMENT(locations_num, 2, available_loc_num);
int num = MIN(locations_num, players_num + ui_players_num);
/* shift in the positions arrays */
int di = rand() % available_loc_num;
struct loc chosen_loc [MAX_AVLBL_LOC];
i = 0;
while (i < num) {
int ii = (i + di + available_loc_num) % available_loc_num;
int x = loc_arr[ii].i;
int y = loc_arr[ii].j;
chosen_loc[i].i = x;
chosen_loc[i].j = y;
g->tiles[x][y].cl = castle;
/* place mines nearby */
int dir = rand() % DIRECTIONS;
int ri = dirs[dir].i;
int rj = dirs[dir].j;
int m = 1;
int mine_i = x + m*ri;
int mine_j = y + m*rj;
g->tiles[mine_i][mine_j].cl = mine;
g->tiles[mine_i][mine_j].pl = NEUTRAL;
mine_i = x - 2*m*ri;
mine_j = y - 2*m*rj;
g->tiles[mine_i][mine_j].cl = mine;
g->tiles[mine_i][mine_j].pl = NEUTRAL;
mine_i = x - m*ri;
mine_j = y - m*rj;
g->tiles[mine_i][mine_j].cl = grassland;
g->tiles[mine_i][mine_j].pl = NEUTRAL;
i++;
}
/* eval locations */
int eval_result[] = {0, 0, 0, 0, 0, 0, 0};
int loc_index[] = {0, 1, 2, 3, 4, 5, 6};
eval_locations(g, chosen_loc, eval_result, num);
/* sort in increasing order */
sort(eval_result, loc_index, num);
/* Compute inequality */
if (ineq != RANDOM_INEQUALITY)
{
float avg = 0;
for(i=0; i50) return -1; break;
case 1: if (x<=50 || x>100) return -1; break;
case 2: if (x<=100 || x>250) return -1; break;
case 3: if (x<=250 || x>500) return -1; break;
case 4: if (x<=500) return -1; break;
}
}
/* suffled computer players */
int *sh_players_comp = malloc(sizeof(int)*players_num);
for(i=0; i 0) {
int select = IN_SEGMENT(num - conditions, 0, num-1);
ihuman = loc_index[select];
}
i = 0;
while (i < num) {
int ii = loc_index[i];
int x = chosen_loc[ ii ].i;
int y = chosen_loc[ ii ].j;
/*
if (human_player != NEUTRAL && ihuman == ii)
g->tiles[x][y].pl = human_player;
else
g->tiles[x][y].pl = sh_players[i];
*/
if (ui_players_num > 1) {
g->tiles[x][y].pl = sh_players[i];
}
else {
if (ii == ihuman)
g->tiles[x][y].pl = ui_players[0];
else
g->tiles[x][y].pl = sh_players_comp[i];
}
g->tiles[x][y].units[ g->tiles[x][y].pl ][citizen] = 10;
i++;
}
/* free allocated memory */
free(sh_players);
return 0;
}
/* helper */
void floodfill (struct grid *g, int u[MAX_WIDTH][MAX_HEIGHT], int x, int y, int val) {
if (x < 0 || x >= g->width || y < 0 || y >= g->height || is_inhabitable(g->tiles[x][y].cl) == 0 ||
u[x][y] == val) {
return;
}
u[x][y] = val;
int k;
for (k = 0; kwidth; ++i)
for(j=0; jheight; ++j)
m[i][j] = 0;
int colored = 0;
for(i=0; iwidth; ++i) {
for(j=0; jheight; ++j) {
if (g->tiles[i][j].pl != NEUTRAL) {
if (colored && m[i][j] == 0) return 0;
colored = 1;
floodfill(g, m, i, j, 1);
}
}
}
return 1;
}
void flag_grid_init(struct flag_grid *fg, int w, int h) {
fg->width = MIN(w, MAX_WIDTH);
fg->height = MIN(h, MAX_HEIGHT);
int i, j;
for(i=0; iwidth; ++i) {
for(j=0; jheight; ++j) {
fg->flag[i][j] = FLAG_OFF;
fg->call[i][j] = 0;
}
}
}
void spread (struct grid *g, int u[MAX_WIDTH][MAX_HEIGHT], int v[MAX_WIDTH][MAX_HEIGHT], int x, int y, int val, int factor) {
if (x < 0 || x >= g->width || y < 0 || y >= g->height || is_inhabitable(g->tiles[x][y].cl) == 0) {
return;
}
int d = val - u[x][y];
if (d > 0) {
v[x][y] = MAX(0, v[x][y] + d * factor);
u[x][y] += d;
int k;
for (k = 0; k= g->width || y < 0 || y >= g->height || v[x][y] == val) {
return;
}
v[x][y] = val;
int k;
for (k = 0; k= g->width || y < 0 || y >= g->height ||
is_inhabitable(g->tiles[x][y].cl) == 0 || fg->flag[x][y] == FLAG_ON) {
return;
}
int u[MAX_WIDTH][MAX_HEIGHT];
int i, j;
for(i=0; i< MAX_WIDTH; ++i){
for(j=0; jflag[x][y] = FLAG_ON;
spread(g, u, fg->call, x, y, val, 1);
}
void remove_flag (struct grid *g, struct flag_grid *fg, int x, int y, int val) {
// exit if
if (x < 0 || x >= g->width || y < 0 || y >= g->height ||
is_inhabitable(g->tiles[x][y].cl) == 0 || fg->flag[x][y] == FLAG_OFF) {
return;
}
int u[MAX_WIDTH][MAX_HEIGHT];
int i, j;
for(i=0; i< MAX_WIDTH; ++i){
for(j=0; jflag[x][y] = FLAG_OFF;
spread(g, u, fg->call, x, y, val, -1);
}
void remove_flags_with_prob (struct grid *g, struct flag_grid *fg, float prob) {
int i, j;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
if (fg->flag[i][j] && (float)rand() / RAND_MAX <= prob) {
remove_flag(g, fg, i, j, FLAG_POWER);
}
}
}
}
grid.h 0000644 0001750 0001750 00000013435 12170266401 012061 0 ustar sicness sicness /******************************************************************************
Curse of War -- Real Time Strategy Game for Linux.
Copyright (C) 2013 Alexey Nikolaev.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#ifndef _GRID_H
#define _GRID_H
#include
#include
#include
#include "common.h"
#define FLAG_ON 1
#define FLAG_OFF 0
#define FLAG_POWER 8
#define RANDOM_INEQUALITY -1
#define MAX_AVLBL_LOC 7
/* enum unit_class
*
* Units/Creatures that can be controlled by the players:
* Only citizens are available. */
enum unit_class { citizen=0 };
/* enum tile_class
*
* Territory classes:
* mountain is a natural barrier
* mine is a source of gold
* grassland is a habitable territory that does not have cities
* village, town, and castle are three kinds of cities with different population growth rate
* (castles have the highest rate, while villages have the lowest).
*
* */
enum tile_class { abyss=0, mountain=1, mine=2, grassland=3, village=4, town=5, castle=6 };
/* is_a_city(t)
returns 1 for village, town, or castle */
int is_a_city(enum tile_class t);
/* is_inhabitable(t)
returns 1 for grassland, village, town, or castle */
int is_inhabitable(enum tile_class t);
int is_visible(enum tile_class t);
enum stencil {st_rhombus, st_rect, st_hex};
/* stencil_avlbl_loc_num (st)
* number of available locations for the stencil st */
int stencil_avlbl_loc_num (enum stencil st);
/* struct tile
*
* Tiles are the smallest pieces of the map.
*
* Components:
* cl is the tile's territory class,
* pl is the id if the player, owner of the tile
* units is the array that contains information about the population of the tile
* (info for all players, and for all unit classes)
*
* */
struct tile {
enum tile_class cl;
int pl;
int units[MAX_PLAYER][MAX_CLASS];
};
/* struct loc
*
* Location.
*
* Components:
* i (horizontal axis)
* j (vertical axis)
* */
struct loc {
int i;
int j;
};
/* There are 6 possible directions to move from a tile. Hexagonal geometry. */
const struct loc dirs[DIRECTIONS];
/* struct grid
*
* 2D Array of tiles + width and height information.
* The map is stored in this structure.
*/
struct grid {
int width;
int height;
struct tile tiles[MAX_WIDTH][MAX_HEIGHT];
};
/* grid_init (&g, w, h)
*
* Initialize the grid g. Set its width to w and height to h.
* It also generates the tiles: Random mountains, mines and cities
*/
void grid_init(struct grid *g, int w, int h);
void apply_stencil(enum stencil st, struct grid *g, int d, struct loc loc[MAX_AVLBL_LOC], int *avlbl_loc_num);
/* conflict (&g, loc_arr, avlbl_loc_num, players, players_num, human_player)
*
* Enhances an already initialized grid.
* Places at most 4 players at the corners of the map, gives them a castle and 2 mines nearby.
* One of those players is always controlled by a human player.
*
* players is the array of the ids of the possible opponents (represented by integers, usually 1 < i < MAX_PLAYER),
* players_num is the size of the players array
*
* locations_num is the number of starting locations (can be equal to 2, 3, or 4)
* human_player is the id of the human player (usually = 1)
*
* conditions = {1, ... number of available locations}, 1 = the best.
*
* ineq = inequality from 0 to 4.
*
*/
int conflict (struct grid *g, struct loc loc_arr[], int available_loc_num,
int players[], int players_num, int locations_num, int ui_players[], int ui_players_num,
int conditions, int ineq);
/* is_conected(&g)
* Check connectedness of the grid */
int is_connected (struct grid *g);
/* struct flag_grid
*
* Similar to the struct grid, but stores information about player's flags.
* Each player has his own struct grid_flag.
*
* flag[i][j] == 1, if there is a flag.
*
* call[i][j] determine the power of attraction to this location.
* Must be updated when flags are added or removed.
*/
struct flag_grid {
int width;
int height;
int flag [MAX_WIDTH][MAX_HEIGHT];
int call [MAX_WIDTH][MAX_HEIGHT];
};
/* flag_grid_init (&fg, w, h)
*
* A simple initialization of the flag grid fg.
*/
void flag_grid_init(struct flag_grid *fg, int w, int h);
/* spread(&g, u, v, x, y, val, factor)
* and
* even(&g, u, x, y, val)
*
* Helper functions, primarily are used for maintaining call[i][j] for flag grids
*/
void spread (struct grid *g, int u[MAX_WIDTH][MAX_HEIGHT], int v[MAX_WIDTH][MAX_HEIGHT], int x, int y, int val, int factor);
void even (struct grid *g, int v[MAX_WIDTH][MAX_HEIGHT], int x, int y, int val);
/* add_flag (&g, &fg, x, y, v)
*
* Adds a flag to the flag grid fg at the location (x,y) with power v.
*/
void add_flag (struct grid *g, struct flag_grid *fg, int x, int y, int val);
/* remove_flag (&g, &fg, x, y, v)
*
* Removes a flag from the flag grid fg at the location (x,y) with power v.
*/
void remove_flag (struct grid *g, struct flag_grid *fg, int x, int y, int val);
/* remove_flags_with_prob (&g, &fg, prob)
*
* Iterates over all tiles, and removes flags with probability prob.
* That is, it removes all flags if prob==1.
*/
void remove_flags_with_prob (struct grid *g, struct flag_grid *fg, float prob);
#endif
king.c 0000644 0001750 0001750 00000025116 12170430667 012065 0 ustar sicness sicness /******************************************************************************
Curse of War -- Real Time Strategy Game for Linux.
Copyright (C) 2013 Alexey Nikolaev.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#include "king.h"
#include
int build (struct grid *g, struct country *c, int pl, int i, int j) {
if (i>=0 && i < g->width && j>=0 && jheight && g->tiles[i][j].pl == pl) {
int price = 0;
enum tile_class cl = grassland;
switch (g->tiles[i][j].cl) {
case grassland: price = PRICE_VILLAGE; cl = village; break;
case village: price = PRICE_TOWN; cl = town; break;
case town: price = PRICE_CASTLE; cl = castle; break;
default: return -1;
}
if (c->gold >= price) { g->tiles[i][j].cl = cl; c->gold -= price; return 0; }
}
return -1;
}
int degrade (struct grid *g, int i, int j) {
if (i>=0 && i < g->width && j>=0 && jheight) {
enum tile_class cl = grassland;
switch (g->tiles[i][j].cl) {
case village: cl = grassland; break;
case town: cl = village; break;
case castle: cl = town; break;
default: return -1;
}
g->tiles[i][j].cl = cl;
return 0;
}
return -1;
}
void king_evaluate_map (struct king *k, struct grid *g, enum config_dif dif) {
int i, j;
int u [MAX_WIDTH][MAX_HEIGHT];
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
u[i][j] = 0;
k->value[i][j] = 0;
}
}
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
if (is_inhabitable(g->tiles[i][j].cl)) k->value[i][j] += 1;
switch (k->strategy) {
case persistent_greedy:
if (is_inhabitable(g->tiles[i][j].cl)) k->value[i][j] += 1;
break;
default: ;
}
switch (g->tiles[i][j].cl) {
case castle:
if(k->strategy == noble)
spread (g, u, k->value, i, j, 32, 1);
else
spread (g, u, k->value, i, j, 16, 1);
even(g, u, i, j, 0);
break;
case town:
spread (g, u, k->value, i, j, 8, 1);
even(g, u, i, j, 0);
break;
case village:
if(k->strategy == noble)
spread (g, u, k->value, i, j, 2, 1);
else
spread (g, u, k->value, i, j, 4, 1);
even(g, u, i, j, 0);
break;
case mine: {
int d;
for(d=0; dstrategy == midas)
spread (g, u, k->value, ii, jj, 8, 1);
else
spread (g, u, k->value, ii, jj, 4, 1);
even (g, u, ii, jj, 0);
}
};
break;
default: ;
}
}
}
/* dumb down kings */
int x;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
switch(dif) {
case dif_easiest:
x = k->value[i][j] / 4;
x = x + rand()%7 - 3;
k->value[i][j] = MAX(0, x);
break;
case dif_easy:
x = k->value[i][j] / 2;
x = x + rand()%3 - 1;
k->value[i][j] = MAX(0, x);
break;
default: ;
}
}
}
}
void king_init (struct king *k, int pl, enum strategy strat, struct grid *g, enum config_dif dif) {
k->pl = pl;
k->strategy = strat;
//king_evaluate_map(k, g, dif);
}
int builder_default (struct king *k, struct country *c, struct grid *g, struct flag_grid *fg) {
int i, j;
int i_best = 0, j_best = 0;
float v_best = 0.0;
float v;
int n;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
int ok = 0;
if ( g->tiles[i][j].pl == k->pl && is_inhabitable(g->tiles[i][j].cl) ) {
ok = 1;
int di, dj;
for(n=0; n= 0 && i+di < g->width &&
j+dj >= 0 && j+dj < g->height &&
is_inhabitable (g->tiles[i+di][j+dj].cl) ) {
ok = ok && (g->tiles[i+di][j+dj].pl == k->pl);
}
}
}
int army = g->tiles[i][j].units[k->pl][citizen];
int enemy = 0;
int p;
for (p=0; ppl)
enemy = enemy + g->tiles[i][j].units[p][citizen];
}
int base = 1.0;
switch (g->tiles[i][j].cl) {
case grassland: base = 1.0; break;
case village: base = 8.0; break;
case town: base = 32.0; break;
default: base = 0.0;
}
if (k->strategy == midas)
base *= (k->value[i][j] + 10);
v = ok * base * (MAX_POP - army);
if (army < MAX_POP / 10) v = 0.0;
if (v > 0.0 && v > v_best) {
i_best = i;
j_best = j;
v_best = v;
}
}
}
if (v_best > 0.0)
return build(g, c, k->pl, i_best, j_best);
else
return -1;
}
/* Auxiliary functions for different kings' strategies: */
void action_aggr_greedy (struct king *k, struct grid *g, struct flag_grid *fg) {
int i, j;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
if (fg->flag[i][j])
remove_flag(g, fg, i, j, FLAG_POWER);
// estimate the value of the grid point (i,j)
int army = g->tiles[i][j].units[k->pl][citizen];
int enemy = 0;
int p;
for (p=0; ppl)
enemy = enemy + g->tiles[i][j].units[p][citizen];
}
float v = (float) k->value[i][j] * (2.0*enemy - army) * pow(army, 0.5);
if (v > 5000)
add_flag(g, fg, i, j, FLAG_POWER);
}
}
}
void action_one_greedy (struct king *k, struct grid *g, struct flag_grid *fg) {
int i, j;
int i_best = 0, j_best = 0;
float v_best = -1.0;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
if (fg->flag[i][j])
remove_flag(g, fg, i, j, FLAG_POWER);
// estimate the value of the grid point (i,j)
int army = g->tiles[i][j].units[k->pl][citizen];
int enemy = 0;
int p;
for (p=0; ppl)
enemy = enemy + g->tiles[i][j].units[p][citizen];
}
float v = (float) k->value[i][j] * (5.0*enemy - army) * pow(army, 0.5);
if (v > v_best && v > 5000) {v_best = v; i_best = i; j_best = j;}
}
}
if (v_best > 0)
add_flag(g, fg, i_best, j_best, FLAG_POWER);
}
void action_persistent_greedy (struct king *k, struct grid *g, struct flag_grid *fg) {
int i, j;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
// estimate the value of the grid point (i,j)
int army = g->tiles[i][j].units[k->pl][citizen];
int enemy = 0;
int p;
for (p=0; ppl)
enemy = enemy + g->tiles[i][j].units[p][citizen];
}
float v1 = (float) k->value[i][j] * (2.5*enemy - army) * pow(army, 0.7);
// weak opportunist
float v2 = (float) k->value[i][j] * (MAX_POP - (enemy - army)) * pow(army, 0.7) * 0.5;
if (enemy <= army) v2 = -10000;
float v = MAX(v1,v2);
if (fg->flag[i][j] == FLAG_ON) {
if (v < 1000)
remove_flag(g, fg, i, j, FLAG_POWER);
}
else {
if (v > 9000)
add_flag(g, fg, i, j, FLAG_POWER);
}
}
}
}
void action_opportunist (struct king *k, struct grid *g, struct flag_grid *fg) {
int i, j;
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
if (fg->flag[i][j])
remove_flag(g, fg, i, j, FLAG_POWER);
// estimate the value of the grid point (i,j)
int army = g->tiles[i][j].units[k->pl][citizen];
int enemy = 0;
int p;
for (p=0; ppl)
enemy = enemy + g->tiles[i][j].units[p][citizen];
}
float v = (float) k->value[i][j] * (MAX_POP - (enemy - army)) * pow(army, 0.5);
if (enemy > army && v > 7000)
add_flag(g, fg, i, j, FLAG_POWER);
}
}
}
/* Priority array */
#define MAX_PRIORITY 32
/* special no-location */
const struct loc no_loc = {-1, -1};
/* initialize array of locations */
void init_locval(struct loc loc[MAX_PRIORITY], int val[MAX_PRIORITY], int len) {
int i;
for(i=0; i=vx; ++i);
if (ii; --j){
loc[j] = loc[j-1];
val[j] = val[j-1];
}
loc[i] = lx;
val[i] = vx;
}
}
void action_noble (struct king *k, struct grid *g, struct flag_grid *fg) {
int i, j;
/* number of flags */
int locval_len = 5;
struct loc loc[MAX_PRIORITY];
int val[MAX_PRIORITY];
init_locval(loc, val, locval_len);
for (i=0; iwidth; ++i) {
for (j=0; jheight; ++j) {
if (fg->flag[i][j])
remove_flag(g, fg, i, j, FLAG_POWER);
// estimate the value of the grid point (i,j)
int army = g->tiles[i][j].units[k->pl][citizen];
int enemy = 0;
int p;
for (p=0; ppl)
enemy = enemy + g->tiles[i][j].units[p][citizen];
}
float v = (float) k->value[i][j] * (MAX_POP - (enemy - army)) * pow(army, 0.5);
if (enemy > army && v > 7000) {
//add_flag(g, fg, i, j, FLAG_POWER);
struct loc lx = {i,j};
insert_locval(loc, val, locval_len, lx, (int)v);
}
}
}
for (i=0; i0; ++i){
add_flag(g, fg, loc[i].i, loc[i].j, FLAG_POWER);
}
}
void place_flags (struct king *k, struct grid *g, struct flag_grid *fg) {
switch (k->strategy) {
case aggr_greedy: action_aggr_greedy(k, g, fg); break;
case one_greedy: action_one_greedy(k, g, fg); break;
case persistent_greedy: action_persistent_greedy(k, g, fg); break;
case opportunist: action_opportunist(k, g, fg); break;
case noble: action_noble(k, g, fg); break;
case midas: break;
default: ;
}
}
king.h 0000644 0001750 0001750 00000006320 12170266401 012057 0 ustar sicness sicness /******************************************************************************
Curse of War -- Real Time Strategy Game for Linux.
Copyright (C) 2013 Alexey Nikolaev.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#ifndef _KING_H
#define _KING_H
#include "grid.h"
/*
struct country
Data about each country
*/
struct country {
long gold;
};
#define PRICE_VILLAGE 150
#define PRICE_TOWN 300
#define PRICE_CASTLE 600
/*
build(&g, &c, pl, i, j)
build a village, upgrade a village to a town, or upgrade a town to a castle.
g and c are struct grid and struct country,
pl is the player id (