Initial commit

This commit is contained in:
KingOfDog 2019-04-08 21:54:07 +02:00 committed by Marcel
commit 9fe0832d76
23 changed files with 5491 additions and 0 deletions

162
Civilization.js Normal file
View File

@ -0,0 +1,162 @@
class Civilization {
constructor(player, game, x, y, addEnvironment = true, addTownhall = true) {
this.player = player;
this.game = game;
this.origin = {x: x, y: y};
this.uuid = guid();
this.color = player.color;
this.balance = 10;
if(addEnvironment) {
console.log(x, y);
this.spawn();
}
console.log(addTownhall);
if(addTownhall)
this.townhall = new Townhall(this, this.game);
}
spawn() {
const field = this.game.field;
field[this.origin.x][this.origin.y].overtake(this);
field[this.origin.x][this.origin.y].getAdjacentTiles().forEach(tile => tile.overtake(this));
}
destroy() {
this.game.field.forEach(col => {
col.forEach(tile => {
if(tile.claimedBy === this) {
tile.claimedBy = null;
if(tile.object) {
if(tile.object instanceof Warrior)
tile.object.kill();
else if(tile.object instanceof Building)
tile.object.destroy();
else if(tile.object instanceof Tree)
tile.object.cutDown();
}
}
})
})
}
getBalanceChange() {
let balanceChange = this.getSize();
balanceChange += 4 * this.getFarms().length;
for(let tower of this.getTowers()) {
balanceChange -= tower.costsPerRound;
}
for(let troop of this.getTroops()) {
balanceChange -= troop.costsPerRound;
}
balanceChange -= this.getTreeCount();
return balanceChange;
}
getCivilizationTiles() {
const tiles = [];
for(let x = 0; x < this.game.fieldSize.x; x++) {
for(let y = 0; y < this.game.fieldSize.y; y++) {
if(!!this.game.field[x][y].claimedBy && this.game.field[x][y].claimedBy.uuid === this.uuid) {
tiles.push(this.game.field[x][y]);
}
}
}
return tiles;
}
getFarms() {
return Object.values(this.game.buildings).filter(building => building instanceof Farm && building.civilization === this);
}
getSize() {
return this.getCivilizationTiles().length;
}
getTowers() {
return [];
}
getTreeCount() {
return this.getCivilizationTiles().filter(tile => !!tile.object && tile.object instanceof Tree).length;
}
getTroops() {
return Object.values(this.game.warriors).filter(warrior => warrior.civilization === this);
}
processRound() {
this.balance += this.getBalanceChange();
if(this.balance <= 0) {
this.getTroops().forEach(troop => {
troop.kill();
});
}
}
select(selected) {
this.isSelected = selected;
// TODO: Update UI
}
split() {
let tiles = this.getCivilizationTiles();
const initialSize = tiles.length;
console.log('hallo welt', tiles.length, tiles[0].getAdjacentCivilizationTiles(this).length);
if(tiles.length === tiles[0].getAdjacentCivilizationTiles(this).length + 1 && !!this.townhall)
return;
const oldIndex = game.civilizations[this.player.uuid].findIndex(civ => civ.uuid === this.uuid);
game.civilizations[this.player.uuid].splice(oldIndex, 1);
if(tiles.length === 0)
return;
const newCivs = [];
if(!!this.townhall)
this.townhall.destroy();
while(tiles.length > 0) {
console.log('hey');
const civTiles = [tiles[0], ...tiles[0].getAdjacentCivilizationTiles(this)];
tiles = tiles.filter(tile => !civTiles.includes(tile));
const availableTiles = civTiles.filter(tile => !tile.object);
let newOrigin = civTiles[Math.floor(Math.random() * civTiles.length)];
if(availableTiles.length > 0) {
newOrigin = availableTiles[Math.floor(Math.random() * availableTiles.length)];
}
console.log(availableTiles.length > 0);
const newCivilization = game.addCivilization(this.player, newOrigin.pos.x, newOrigin.pos.y, this.constructor !== CivilizationAI, false, availableTiles.length > 0);
newCivs.push(newCivilization);
newCivilization.balance = Math.round(this.balance * (civTiles.length / initialSize));
civTiles.forEach(tile => {
tile.claimedBy = newCivilization;
if(!!tile.object && (tile.object instanceof Warrior || tile.object instanceof Tower)) {
tile.object.civilization = newCivilization;
}
});
}
newCivs.forEach(civ => {
});
}
}

14
CivilizationAI.js Normal file
View File

@ -0,0 +1,14 @@
class CivilizationAI extends Civilization {
constructor(player, game, x, y, addEnvironment, addTownhall) {
super(player, game, x, y, addEnvironment, addTownhall);
}
processRound() {
const availableWarriors = Object.values(this.game.warriors).filter(warrior => warrior.civilization === this);
availableWarriors.forEach(warrior => {
});
super.processRound();
}
}

550
Game.js Normal file
View File

@ -0,0 +1,550 @@
/**
* @property {Node} btnTroops
* @property {Node} btnFarmer
* @property {Node} btnHoplite
* @property {Node} btnServant
* @property {Node} btnKnight
* @property {Node} btnTowers
* @property {Node} btnFarm
* @property {Node} btnTowerSmall
* @property {Node} btnTowerBig
*/
class Game {
constructor() {
this.fieldSize = {x: 20, y: 10};
this.scale = 1;
this.deltaX = 0;
this.deltaY = 0;
this.warriors = {};
this.buildings = {};
this.field = [];
this.civilizations = {};
this.trees = {};
this.round = 1;
this.hexagonSize = {width: 27.85714285714286, padding: 32.16760145166612, offset: 6.9285714285};
this.initializeElements();
this.resize();
this.createHexagons();
this.registerEventListener();
}
initializeElements() {
this.elements = {};
this.container = body.createChild('div', 'gameContainer');
this.gameStats = this.container.createChild('div', 'gameStats');
this.viewContainer = this.container.createChild('div', 'viewContainer');
this.view = this.viewContainer.createChild('div', 'view');
this.playground = this.view.createChild('ul', 'playground');
this.btns = this.container.createChild('div', 'controls');
this.elements.roundParent = this.gameStats.createChild('h3', ['gameStat', 'left'], 'Round: ');
this.elements.round = this.elements.roundParent.createChild('span', 'count', '1');
this.elements.balanceParent = this.gameStats.createChild('h3', ['gameStat', 'center']);
this.elements.balance = this.elements.balanceParent.createChild('span', 'count');
this.elements.balanceChange = this.elements.balanceParent.createChild('span', ['count', 'text-grey']);
this.elements.player = this.gameStats.createChild('h3', ['gameStat', 'right'], 'Player ' + player.color.toUpperCase());
this.nextRoundBtn = this.btns.createChild('button', ['btn', 'btn-primary', 'btn-nextRound'], 'Next Round');
this.nextRoundBtn.addEventListener('click', () => {
this.nextRound()
});
[this.btnTroops, this.btnFarmer, this.btnHoplite, this.btnServant, this.btnKnight] = createTroopsBtn(this.btns);
[this.btnFarmer, this.btnHoplite, this.btnServant, this.btnKnight].forEach((btn, index) => {
const types = [Farmer, Hoplite, Servant, Knight];
this.addBtnClickListener(btn, types[index], 'addWarrior');
});
[this.btnTowers, this.btnFarm, this.btnTowerSmall, this.btnTowerBig] = createTowersBtn(this.btns);
[this.btnFarm, this.btnTowerSmall, this.btnTowerBig].forEach((btn, index) => {
const types = [Farm, SmallTower, BigTower];
this.addBtnClickListener(btn, types[index], 'addBuilding');
});
}
addBtnClickListener(btn, className, functionName) {
btn.addEventListener('click', () => {
if (!!this.selectedCivilization) {
const newObj = {
type: 'new',
class: className,
function: functionName,
civilization: this.selectedCivilization
};
if (!!this.draggingElement &&
!!this.selectedObject &&
this.selectedObject.type === 'new' &&
this.selectedObject.class === newObj.class &&
this.selectedObject.civilization === newObj.civilization)
return;
this.deselectObject();
this.draggingElement = className.getDraggingObject(this);
this.selectedObject = newObj;
}
});
}
addBuilding(x, y, civilization, type) {
const tile = this.field[x][y];
console.log(tile, type);
if (type.initialCosts > civilization.balance)
return false;
console.log('creatio');
if (!tile.claimedBy || tile.claimedBy.player !== civilization.player) // Is non-friendly territory
return false;
if (tile.object !== null)
return false;
console.log('success');
const building = new type(x, y, this, civilization);
tile.object = building;
this.buildings[building.uuid] = building;
return true;
}
addCivilization(player, x, y, realPlayer = false, addEnvironment = true, addTownhall = true) {
if (!this.civilizations[player.uuid])
this.civilizations[player.uuid] = [];
const CivilizationType = realPlayer ? Civilization : CivilizationAI;
const newCivilization = new CivilizationType(player, this, x, y, addEnvironment, addTownhall);
this.civilizations[player.uuid].push(newCivilization);
return newCivilization;
}
addTree(x, y) {
if (!!this.field[x][y].object)
return;
const tree = new Tree(x, y, this);
this.trees[tree.uuid] = tree;
}
addWarrior(x, y, civilization, type) {
const tile = this.field[x][y];
if (type.initialCosts > civilization.balance)
return false;
const warrior = new type(x, y, this, civilization);
const player = civilization.player;
if (!!tile.claimedBy && tile.claimedBy.player === player) { // Is friendly territory
if (!!tile.object) { // Is object on the tile?
if (tile.object instanceof Tree) {
tile.object.cutDown();
} else if (tile.object instanceof Warrior) {
if (tile.object.rank <= 4 - warrior.rank) {
tile.object.merge(warrior);
}
return false;
} else if (tile.object instanceof Building) {
return false;
}
}
} else { // Is hostile territory
if (!tile.getAdjacentTiles().find(adjTile => adjTile.claimedBy === civilization)) { // There isn't any tile of the given civilization nearby
return false;
}
// TODO: Check adjacent tiles
if (!!tile.object) {
if (tile.object instanceof Tree) {
tile.object.cutDown();
} else if (tile.object instanceof Warrior) {
if (tile.object.rank < warrior.rank) {
tile.object.kill();
} else {
return false;
}
} else if (tile.object instanceof Building) {
if (tile.object.rank < warrior.rank) {
tile.object.destroy();
} else {
return false;
}
}
}
}
this.warriors[warrior.uuid] = warrior;
tile.object = warrior;
tile.overtake(civilization);
return true;
}
createHexagons() {
for (let x = 0; x < this.fieldSize.x; x++) {
this.field.push([]);
}
const margin = Math.round(this.hexagonSize.width * .1);
for (let y = 0; y < this.fieldSize.y; y++) {
for (let x = 0; x < this.fieldSize.x; x++) {
const listItem = this.playground.createChild('li');
listItem.style.width = this.hexagonSize.width + "px";
listItem.style.marginRight = margin + 'px';
listItem.style.paddingBottom = this.hexagonSize.padding + "px";
if (y % 2 !== 0) {
if (x === 0)
listItem.style.marginLeft = margin / 2 + 'px';
listItem.style.marginTop = listItem.style.marginBottom = -2 * margin + "px";
listItem.classList.add('even-row');
}
listItem.createChild('div', ['hexagon', 'grey']);
this.field[x][y] = new Tile(this, listItem, x, y);
}
}
}
calculateHexagonSize() {
const paddingRatio = this.hexagonSize.padding / this.hexagonSize.width;
const offsetRatio = this.hexagonSize.offset / this.hexagonSize.width;
this.container.style.width = '';
const widthPercentage = (100 / this.fieldSize.x * .9);
let width = Math.round(widthPercentage / 100 * this.container.clientWidth);
if (width % 2 !== 0)
width -= 1;
this.hexagonSize.width = width;
this.hexagonSize.padding = this.hexagonSize.width * paddingRatio;
this.hexagonSize.offset = this.hexagonSize.width * offsetRatio;
}
increaseRound() {
this.round++;
this.elements.round.innerText = this.round;
}
nextRound() {
// TODO: Simulate other players
// Lets the trees breed
Object.values(this.trees).forEach(tree => {
tree.breed();
});
// Handles finances of the civilizations
Object.values(this.civilizations).forEach(civilizations => {
civilizations.forEach(civilization => {
civilization.processRound();
});
});
// Sets all warriors waiting
Object.values(this.warriors).forEach(warrior => {
if (warrior.civilization.player === player) {
warrior.setNeedsAction(true);
}
});
this.increaseRound();
this.updateBalance();
}
registerEventListener() {
let mouseStartX;
let mouseStartY;
let startX;
let startY;
this.view.onmousedown = e => {
mouseStartX = e.clientX;
mouseStartY = e.clientY;
startX = parseInt(this.view.style.left.slice(0, this.view.style.left.length - 2)) || 0;
startY = parseInt(this.view.style.top.slice(0, this.view.style.top.length - 2)) || 0;
};
this.view.onmousemove = e => {
if (!!mouseStartX && !!mouseStartY) {
let deltaX = e.clientX - mouseStartX + startX;
let deltaY = e.clientY - mouseStartY + startY;
this.repositionView(deltaX, deltaY);
}
};
this.view.onmouseup = e => {
mouseStartX = undefined;
mouseStartY = undefined;
};
this.viewContainer.onwheel = e => {
const prevScale = this.scale;
if (e.deltaY < 0) {
this.scale *= 1.1;
} else {
this.scale /= 1.1;
}
this.scale = Math.max(this.scale, 1);
this.scale = Math.min(this.scale, 10);
const deltaScale = this.scale / prevScale;
this.view.style.transform = `scale(${this.scale})`;
if (!!this.draggingElement) {
let dragging = this.draggingElement.childNodes[0];
dragging.style.width = deltaScale * dragging.clientWidth + 'px';
dragging.style.height = deltaScale * dragging.clientHeight + 'px';
}
let centerY = -(this.view.clientHeight - this.viewContainer.clientHeight);
if (this.view.clientHeight < this.viewContainer.clientHeight) {
centerY /= 2;
}
this.view.classList.add('zooming');
this.repositionView(this.deltaX * deltaScale, centerY - (centerY - this.deltaY) * deltaScale);
setTimeout(() => this.view.classList.remove('zooming'), 200);
};
document.onmousemove = (ev) => {
if (!!this.draggingElement) {
const draggingContainer = this.draggingElement;
const dragging = draggingContainer.childNodes[0];
draggingContainer.style.left = ev.clientX + 'px';
draggingContainer.style.top = ev.clientY + 'px';
if (!!prevMouseEvent) {
clearTimeout(timeout);
const vel = (ev.clientX - prevMouseEvent.clientX) / (ev.timeStamp - prevMouseEvent.timeStamp);
let rotation = Math.min(Math.max(vel * 80, -80), 80);
dragging.style.transform = 'rotateZ(' + rotation + 'deg)';
timeout = setTimeout(() => {
dragging.style.transform = 'rotateZ(0deg)';
}, 50);
}
prevMouseEvent = ev;
}
};
this.container.addEventListener('contextmenu', (ev) => ev.preventDefault());
window.addEventListener('resize', () => this.resize());
}
repositionView(deltaX, deltaY) {
const maxDeltaX = (this.view.clientWidth * this.scale - this.viewContainer.clientWidth) / 2;
const maxDeltaTop = this.view.clientHeight * (this.scale - 1) / 2;
let maxDeltaBottom = (this.view.clientHeight - this.viewContainer.clientHeight);
if (this.view.clientHeight < this.viewContainer.clientHeight) {
maxDeltaBottom /= 2;
}
deltaX = Math.min(deltaX, maxDeltaX);
deltaX = Math.max(deltaX, -maxDeltaX);
deltaY = Math.min(deltaY, maxDeltaTop);
deltaY = Math.max(deltaY, -maxDeltaBottom);
this.deltaX = deltaX;
this.deltaY = deltaY;
this.view.style.left = deltaX + 'px';
this.view.style.top = deltaY + 'px';
}
resize() {
this.calculateHexagonSize();
const containerWidth = Math.round(this.hexagonSize.width / .9) * this.fieldSize.x;
this.container.style.width = containerWidth + 'px';
const margin = Math.round(this.hexagonSize.width * .1);
this.field.forEach((col, x) => {
col.forEach((tile, y) => {
tile.element.style.width = this.hexagonSize.width + "px";
tile.element.style.marginRight = margin + 'px';
tile.element.style.paddingBottom = this.hexagonSize.padding + "px";
if (y % 2 !== 0) {
if (x === 0)
tile.element.style.marginLeft = margin / 2 + 'px';
tile.element.style.marginTop = tile.element.style.marginBottom = -2 * margin + "px";
}
});
});
// this.view.style.width = containerWidth + this.hexagonSize.width / 2 + 'px';
this.view.style.height = (this.hexagonSize.width / 1.01 * this.scale) * this.fieldSize.y + 'px';
this.field.forEach(col => {
col.forEach(tile => {
if (!!tile.object) {
tile.object.adjustPosition();
}
});
});
this.repositionView(0, 0);
}
selectCivilization(civilization) {
if (!!this.selectedCivilization)
this.selectedCivilization.select(false);
if (!!civilization && civilization.player === player) {
this.selectedCivilization = civilization;
civilization.select(true);
this.btnTroops.classList.remove('disabled');
this.btnTowers.classList.remove('disabled');
} else {
this.selectedCivilization = null;
this.btnTroops.classList.add('disabled');
this.btnTroops.classList.remove('isActive');
this.btnTowers.classList.add('disabled');
this.btnTowers.classList.remove('isActive');
}
this.updateBalance();
}
selectWarrior(warrior) {
this.selectedObject = warrior;
}
updateBalance() {
if (!!this.selectedCivilization && this.selectedCivilization.player === player) {
const balanceChange = this.selectedCivilization.getBalanceChange();
this.elements.balance.innerText = this.selectedCivilization.balance;
this.elements.balanceChange.innerText = balanceChange > 0 ? '+' + balanceChange : balanceChange;
} else {
this.elements.balance.innerText = null;
this.elements.balanceChange.innerText = null;
}
}
deselectObject() {
if (!!this.selectedObject) {
this.selectedObject.isSelected = false;
}
this.selectedObject = null;
if (!!this.draggingElement) {
this.draggingElement.parentNode.removeChild(this.draggingElement);
}
this.draggingElement = null;
}
}
class CivilizationNN extends Civilization {
constructor(player, game, x, y) {
super(player, game, x, y, true, true);
}
}
class GameNN extends Game {
constructor(nn) {
super();
this.ga = nn || new GeneticAlgorithm(10, 4, this);
for (let i = 0; i < this.ga.maxUnits; i++) {
let x;
let y;
let isGood = false;
while(!isGood) {
x = this.ga.random(0, 19);
y = this.ga.random(0, 9);
if (!this.field[x][y].getAdjacentTiles().find(tile => !!tile.claimedBy))
isGood = true;
}
const civ = this.addCivilization(new Player('green'), x, y);
civ.index = i;
}
this.initialCivs = this.civilizations;
this.fotze = true;
if(!nn) {
this.ga.reset();
this.ga.createPopulation();
}
}
addCivilization(player, x, y) {
if (!this.civilizations[player.uuid])
this.civilizations[player.uuid] = [];
const newCivilization = new CivilizationNN(player, this, x, y);
this.civilizations[player.uuid].push(newCivilization);
if(x < this.fieldSize.x - 1)
this.addWarrior(x + 1, y, newCivilization, Farmer);
return newCivilization;
}
nextRound() {
if(Object.values(this.civilizations).length === 0) {
this.ga.evolvePopulation();
this.ga.iteration++;
}
Object.values(this.civilizations).forEach(civilizations => {
civilizations.forEach(civilization => {
civilization.fitness = civilization.getSize();
civilization.score = this.round;
this.ga.activateBrain(civilization);
});
});
super.nextRound();
if(this.round === 1000) {
this.ga.evolvePopulation();
this.ga.iteration++;
Object.values(this.civilizations).forEach(player => {
player.forEach(civ => {
civ.destroy();
});
});
this.civilizations = this.initialCivs;
Object.values(this.civilizations).forEach(player => {
player.forEach(civ => {
civ.spawn();
});
});
this.round = 0;
}
setTimeout(() => {
if (this.fotze)
this.nextRound();
}, 50);
}
}

62
GameElement.js Normal file
View File

@ -0,0 +1,62 @@
class GameElement {
constructor(x, y, game, asset, setObject = true) {
this.game = game;
this.x = Math.min(x, this.game.fieldSize.x - 1);
this.y = Math.min(y, this.game.fieldSize.y - 1);
this.uuid = guid();
this.createElement(asset);
if(setObject)
this.game.field[x][y].object = this;
}
adjustPosition() {
const tile = this.game.field[this.x][this.y].element;
this.displayWidth = Math.round(this.game.hexagonSize.width * .9);
let offsetX = tile.offsetLeft;
let offsetY = tile.offsetTop;
if (this.y % 2 !== 0)
offsetX += Math.round(this.displayWidth * .5);
this.element.style.width = this.displayWidth + 'px';
this.element.style.left = offsetX + 'px';
this.element.style.top = offsetY + 'px';
}
createElement(asset) {
this.element = GameElement.createElement(asset, (this.constructor.name).toLowerCase());
this.adjustPosition();
this.game.view.appendChild(this.element);
}
static createElement(asset, className) {
const element = document.createElement('img');
element.src = 'assets/' + asset;
element.ondragstart = () => {
return false
};
element.ondragend = () => {
return false
};
element.classList.add('gameElement', className);
return element;
}
static getDraggingObject(game, className, image) {
const draggingContainer = body.createChild('div', 'draggingContainer');
const dragging = GameElement.createElement(image, className);
dragging.classList.add('dragging');
dragging.style.width = game.hexagonSize.width * game.scale * 1.1 + 'px';
dragging.style.height = game.hexagonSize.padding * game.scale * 1.1 + 'px';
draggingContainer.appendChild(dragging);
return draggingContainer;
}
update() {
this.adjustPosition();
this.game.updateBalance();
}
}

202
NeuralNetwork.js Normal file
View File

@ -0,0 +1,202 @@
class GeneticAlgorithm {
constructor(maxUnits, topUnits, game) {
this.maxUnits = maxUnits;
this.topUnits = topUnits;
if(this.maxUnits < this.topUnits)
this.topUnits = maxUnits;
this.population = [];
this.SCALE_FACTOR = 200;
this.game = game;
}
reset() {
this.iteration = 1;
this.mutateRate = 1;
this.bestPopulation = 0;
this.bestFitness = 0;
this.bestScore = 0;
}
createPopulation() {
this.population.slice(0, this.population.length);
for(let i = 0; i < this.maxUnits; i++) {
const newUnit = new synaptic.Architect.Perceptron(6 * this.game.fieldSize.x * this.game.fieldSize.y, 35, 5);
newUnit.index = i;
newUnit.fitness = 0;
newUnit.score = 0;
newUnit.isWinner = false;
this.population.push(newUnit);
}
}
activateBrain(civilization) {
// TODO: Inputs
const inputs = [civilization.balance, civilization.getBalanceChange()];
for(let x = 0; x < this.game.fieldSize.x; x++) {
for(let y = 0; y < this.game.fieldSize.y; y++) {
const tile = this.game.field[x][y];
const isFriendly = tile.claimedBy === civilization ? 1 : 0;
const hasObject = !!tile.object ? 1 : 0;
const canAttack = tile.object instanceof Warrior ? 1 : 0;
const rank = !!tile.object ? tile.object.rank / 4 : 0;
inputs.push(x, y, isFriendly, hasObject, canAttack, rank);
}
}
console.log(civilization.index);
const outputs = this.population[civilization.index].activate(inputs);
console.log(outputs);
const type = outputs[0];
const x = Math.round(outputs[1] * (this.game.fieldSize.x - 1));
const y = Math.round(outputs[2] * (this.game.fieldSize.y - 1));
const tile = this.game.field[x][y];
if(type < .5) {
if(tile.object && tile.claimedBy === civilization && tile.object instanceof Warrior) {
const deltaX = Math.round(outputs[3] * 4 - 2) / 4;
const deltaY = Math.round(outputs[4] * 4 - 2) / 4;
const targetX = x + deltaX;
const targetY = y + deltaY;
tile.object.move(targetX, targetY);
console.log(deltaX, deltaY);
}
} else {
const itemType = outputs[3];
console.log('hey');
if(itemType > .5) {
console.log('ho');
if(!tile.getAdjacentTiles().find(tile => tile.claimedBy === civilization))
return;
const troopType = Math.round(outputs[4] * 3);
const troopTypes = [Farmer, Hoplite, Servant, Knight];
this.game.addWarrior(x, y, civilization, troopTypes[troopType]);
} else {
if(tile.claimedBy !== civilization)
return;
const buildingType = Math.round(outputs[4] * 2);
const buildingTypes = [Farm, SmallTower, BigTower];
console.log('ho2', buildingType, tile);
this.game.addBuilding(x, y, civilization, buildingTypes[buildingType]);
}
}
}
evolvePopulation() {
const winners = this.selection();
if(this.mutateRate === 1 && winners[0].fitness < 0) {
this.createPopulation();
} else {
this.mutateRate = .2;
}
for(let i = this.topUnits; i < this.maxUnits; i++) {
let parentA, parentB, offspring;
if(i === this.topUnits) {
parentA = winners[0].toJSON();
parentB = winners[1].toJSON();
offspring = this.crossOver(parentA, parentB);
} else if(i < this.maxUnits - 2) {
parentA = this.getRandomUnit(winners).toJSON();
parentB = this.getRandomUnit(winners).toJSON();
offspring = this.crossOver(parentA, parentB);
} else {
offspring = this.getRandomUnit(winners).toJSON();
}
offspring = this.mutation(offspring);
const newUnit = synaptic.Network.fromJSON(offspring);
newUnit.index = this.population[i].index;
newUnit.fitness = 0;
newUnit.score = 0;
newUnit.isWinner = false;
this.population[i] = newUnit;
}
if(winners[0].fitness > this.bestFitness) {
this.bestPopulation = this.iteration;
this.bestFitness = winners[0].fitness;
this.bestScore = winners[0].score;
}
this.population.sort((unitA, unitB) => unitA.index - unitB.index);
}
selection() {
const sortedPopulation = this.population.sort((unitA, unitB) => unitB.fitness - unitA.fitness);
for(let i = 0; i < this.topUnits; i++) {
this.population[i].isWinner = true;
}
return sortedPopulation.slice(0, this.topUnits);
}
crossOver(parentA, parentB) {
const cutPoint = this.random(0, parentA.neurons.length - 19);
for(let i = cutPoint; i < parentA.neurons.length; i++) {
const biasFromParentA = parentA.neurons[i]['bias'];
parentA.neurons[i]['bias'] = parentB.neurons[i]['bias'];
parentB.neurons[i]['bias'] = biasFromParentA;
}
return this.random(0, 1) === 1 ? parentA : parentB;
}
mutation(offspring) {
for(let i = 0; i < offspring.neurons.length; i++) {
offspring.neurons[i]['bias'] = this.mutate(offspring.neurons[i]['bias']);
}
for(let i = 0; i < offspring.connections.length; i++) {
offspring.connections[i]['weight'] = this.mutate(offspring.connections[i]['weight']);
}
return offspring;
}
mutate(gene) {
if(Math.random() < this.mutateRate) {
gene *= 1 + ((Math.random() - .5) * 3 + (Math.random() - .5));
}
return gene;
}
random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
getRandomUnit(array) {
return array[this.random(0, array.length - 1)];
}
normalize(value, max) {
if(value < -max)
value = -max;
else if(value > max)
value = max;
return value / max;
}
}

104
Tile.js Normal file
View File

@ -0,0 +1,104 @@
class Tile {
constructor(game, element, x, y) {
this.game = game;
this.element = element;
this.pos = {x: x, y: y};
this.claimedBy = null;
this.object = null;
this.addClickListener();
}
addClickListener() {
this.element.addEventListener('click', () => this.clickHandler());
}
clickHandler() {
// Checks whether there is an object selected at the moment and if it belongs to the human player
if (!!this.game.selectedObject && this.game.selectedObject.civilization.player === player) {
// Checks whether this tile is a possible destination for the object based on the object type
if (this.game.selectedObject instanceof Warrior) {
this.game.selectedObject.move(this.pos.x, this.pos.y);
return;
}
if (this.game.selectedObject.type === 'new') {
const obj = this.game.selectedObject;
const allowMoveAfterCreation = this.claimedBy === obj.civilization && this.object === null;
const result = this.game[obj.function](this.pos.x, this.pos.y, obj.civilization, obj.class);
if (!result) {
this.game.deselectObject();
return;
}
if (!allowMoveAfterCreation) {
this.object.setNeedsAction(false);
}
} else {
this.object = this.game.selectedObject;
}
this.object.x = this.pos.x;
this.object.y = this.pos.y;
this.game.deselectObject();
this.object.update();
} else if(this.object instanceof Warrior) {
this.object.onClick();
}
this.game.selectCivilization(this.claimedBy);
}
getAdjacentTiles() {
const surroundings = [
[-1, 0],
[0, -1],
[0, 1],
[1, 0],
];
if (this.pos.y % 2 === 0) {
surroundings.push([-1, -1], [-1, 1]);
} else {
surroundings.push([1, -1], [1, 1]);
}
const adjacentTiles = [];
surroundings.forEach(modification => {
const x = this.pos.x + modification[0];
const y = this.pos.y + modification[1];
if (x >= 0 && x < this.game.fieldSize.x && y >= 0 && y < this.game.fieldSize.y)
adjacentTiles.push(this.game.field[x][y]);
});
return adjacentTiles;
}
getAdjacentCivilizationTiles(civilization, excluded = []) {
const surroundings = this.getAdjacentTiles().filter(tile => !excluded.includes(tile));
const matches = [];
surroundings.forEach(tile => {
if(!!tile.claimedBy && tile.claimedBy.uuid === civilization.uuid) {
matches.push(tile);
matches.push(...tile.getAdjacentCivilizationTiles(civilization, [this, ...surroundings, ...matches, ...excluded]));
}
});
return matches;
}
getAvailableAdjacentTiles() {
return this.getAdjacentTiles().filter(tile => !tile.object);
}
overtake(civilization) {
const prevColor = this.getColor();
this.claimedBy = civilization;
this.element.childNodes[0].classList.remove(prevColor);
this.element.childNodes[0].classList.add(this.getColor());
}
getColor() {
return this.claimedBy != null ? this.claimedBy.color : 'grey';
}
}

247
Warrior.js Normal file
View File

@ -0,0 +1,247 @@
class Warrior extends GameElement {
constructor(x, y, game, civilization, asset) {
super(x, y, game, asset, false);
this.civilization = civilization;
this.isSelected = false;
this.civilization.balance -= this.constructor.initialCosts;
this.setNeedsAction(true);
}
onClick() {
if (this.civilization.player !== player)
return;
this.game.selectCivilization(this.civilization);
if (!this.game.selectedObject && this.needsAction) {
this.isSelected = true;
this.game.selectWarrior(this);
this.game.draggingElement = this.constructor.getDraggingObject(this.game);
}
}
static getDraggingObject(game, className = 'farmer', image = 'knight.svg') {
return super.getDraggingObject(game, className, image);
}
getPossibleTiles(player) {
return this.checkSurroundings(this.x, this.y, player);
}
merge(warrior) {
this.rank += warrior.rank || 1;
this.rank = Math.min(this.rank, 4);
warrior.kill();
this.kill();
const ranks = [Farmer, Hoplite, Servant, Knight];
this.civilization.balance += warrior.constructor.initialCosts + this.constructor.initialCosts;
this.game.addWarrior(this.x, this.y, this.civilization, ranks[this.rank - 1]);
// TODO: Switch image
}
move(x, y) {
if (!this.needsAction)
return false;
if (!this.isPossibleDestination(x, y, this.civilization.player))
return false;
this.game.field[this.x][this.y].object = null;
const tile = this.game.field[x][y];
if (!tile.claimedBy || tile.claimedBy.player === this.civilization.player) { // Friendly territory
if (tile.object instanceof Tree) { // Tree
tile.object.cutDown();
} else if (tile.object instanceof Warrior) { // Obstacle is a friendly warrior
tile.object.merge(this);
this.game.deselectObject();
return true;
}
} else { // Hostile territory
// Check surrounding tiles
if(tile.getAdjacentTiles().find(adjTile => adjTile.claimedBy !== this.civilization &&
!!adjTile.object &&
(adjTile.object instanceof Warrior || adjTile.object instanceof Tower) &&
adjTile.object.rank >= this.rank)) {
return false;
}
if (tile.object instanceof Tree) { // No object/troop on tile
tile.object.cutDown();
} else if (tile.object instanceof Warrior) { // Obstacle is an hostile warrior
if (tile.object.rank < this.rank) { // Hostile warrior/tower is weaker than own troop
tile.object.kill();
}
} else if (tile.object instanceof Building) {
if (tile.object.rank < this.rank) {
tile.object.destroy();
}
}
}
this.x = x;
this.y = y;
this.game.deselectObject();
this.setNeedsAction(false);
const prevCiv = tile.claimedBy;
tile.object = this;
tile.overtake(this.civilization);
if(prevCiv instanceof Civilization && prevCiv !== this.civilization) {
console.log(prevCiv.uuid);
prevCiv.split();
}
this.update();
}
checkSurroundings(x, y, player, step = 0, excluded = []) {
const field = this.game.field;
const possibleSurroundings = [];
const neighbours = field[x][y].getAdjacentTiles().filter(tile => !excluded.includes(tile));
neighbours.forEach(neighbour => {
const {x, y} = neighbour.pos;
if (x < 0 || x >= this.game.fieldSize.x || y < 0 || y >= this.game.fieldSize.y)
return;
if (this.isPossibleTile(x, y)) {
possibleSurroundings.push(field[x][y]);
if (step < 4 && !!field[x][y].claimedBy && field[x][y].claimedBy.player === player)
possibleSurroundings.push(...this.checkSurroundings(x, y, player, ++step, [...excluded, ...neighbours, ...possibleSurroundings]));
}
});
return possibleSurroundings;
}
isPossibleTile(x, y) {
const tile = this.game.field[x][y];
if (!!tile.claimedBy && tile.claimedBy.player === this.civilization.player) { // Friendly territory
if (!tile.object || tile.object instanceof Tree) { // No object/troop on tile
return true;
}
if (tile.object instanceof Warrior) { // Obstacle is a friendly warrior
if (tile.object.rank <= 4 - this.rank) { // Warrior isn't yet on max level
return true;
}
}
// Otherwise there is an unavoidable obstacle
} else { // Hostile territory
if (!tile.object || tile.object instanceof Tree) { // No object/troop on tile
// TODO: Check surrounding tiles
return true;
}
if (tile.object instanceof Warrior || tile.object instanceof Building) { // Obstacle is an hostile warrior
if (tile.object.rank < this.rank) { // Hostile warrior/tower is weaker than own troop
// TODO: Check surrounding tiles
return true;
}
}
// Otherwise there is an unavoidable obstacle
}
return false;
}
isPossibleDestination(x, y, player) {
return this.getPossibleTiles(player).includes(this.game.field[x][y]);
}
kill() {
delete this.game.warriors[this.uuid];
this.element.parentNode.removeChild(this.element);
this.game.field[this.x][this.y].object = null;
// TODO: Show gravestone
}
setNeedsAction(needsAction) {
this.needsAction = needsAction;
if (this.civilization.player === player && needsAction) {
this.element.classList.add('waiting');
} else {
this.element.classList.remove('waiting');
}
}
}
class Farmer extends Warrior {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, 'farmer.svg');
this.rank = 1;
this.costsPerRound = 2;
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'farmer', 'farmer.svg');
}
static get initialCosts() {
return 10;
}
}
class Hoplite extends Warrior {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, 'hoplite.svg');
this.rank = 2;
this.costsPerRound = 6;
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'hoplite', 'hoplite.svg');
}
static get initialCosts() {
return 20;
}
}
class Servant extends Warrior {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, '');
this.rank = 3;
this.costsPerRound = 18;
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'servant', 'servant.svg');
}
static get initialCosts() {
return 30;
}
}
class Knight extends Warrior {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, 'knight.svg');
this.rank = 4;
this.costsPerRound = 36;
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'knight', 'knight.svg');
}
static get initialCosts() {
return 40;
}
}

1
assets/farm.svg Normal file
View File

@ -0,0 +1 @@
<svg height="512pt" viewBox="0 -46 512 512" width="512pt" xmlns="http://www.w3.org/2000/svg"><path d="m120 180c-66.167969 0-120 53.832031-120 120s53.832031 120 120 120 120-53.832031 120-120-53.832031-120-120-120zm0 210c-49.625 0-90-40.375-90-90s40.375-90 90-90 90 40.375 90 90-40.375 90-90 90zm0 0"/><path d="m120 255c-24.8125 0-45 20.1875-45 45s20.1875 45 45 45 45-20.1875 45-45-20.1875-45-45-45zm0 60c-8.269531 0-15-6.730469-15-15s6.730469-15 15-15 15 6.730469 15 15-6.730469 15-15 15zm0 0"/><path d="m482 262.980469v-52.980469c0-33.085938-26.914062-60-60-60h-30v-75c0-8.269531 6.730469-15 15-15v-30c-24.8125 0-45 20.1875-45 45v75h-65.839844l-36-120h24.839844v-30h-240v30h30v95.738281c-25.347656 6.554688-49.171875 18.578125-69.550781 35.410157l19.101562 23.132812c26.769531-22.105469 60.667969-34.28125 95.449219-34.28125 82.710938 0 150 67.289062 150 150v45h63.257812c7.164063 42.511719 44.226563 75 88.742188 75 49.625 0 90-40.375 90-90 0-26.59375-11.597656-50.527344-30-67.019531zm-377-142.34375v-90.636719h123.839844l36 120h-45.457032c-28.5-18.945312-62.671874-30-99.382812-30-5.019531 0-10.023438.21875-15 .636719zm149.039062 59.363281h167.960938c16.542969 0 30 13.457031 30 30v35.148438c-9.386719-3.328126-19.484375-5.148438-30-5.148438h-132.296875c-7.925781-22.347656-20.140625-42.679688-35.664063-60zm43.445313 90h57.496094c-11.113281 12.398438-18.839844 27.886719-21.722657 45h-33.257812v-15c0-10.21875-.867188-20.238281-2.515625-30zm124.515625 120c-33.085938 0-60-26.914062-60-60s26.914062-60 60-60 60 26.914062 60 60-26.914062 60-60 60zm0 0"/><path d="m437 330c0 8.285156-6.714844 15-15 15s-15-6.714844-15-15 6.714844-15 15-15 15 6.714844 15 15zm0 0"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

69
assets/farmer.svg Normal file
View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path d="M293.872,30.38C290.012,13,274.514,0,255.972,0s-34.04,13-37.901,30.38c-22.677,3.461-37.948,9.925-37.948,17.337
c-0.001,7.164,14.269,13.446,35.706,16.982c0.2,22.003,18.091,39.78,40.141,39.78c22.05,0,39.942-17.777,40.141-39.78
c21.438-3.537,35.707-9.818,35.707-16.982C331.819,40.307,316.549,33.841,293.872,30.38z"/>
</g>
</g>
<g>
<g>
<path d="M203.644,117.277c-25.349,0-45.675,20.742-45.675,45.76v142.325c0.026,10.684,8.865,19.327,19.543,19.327
c0.016,0,0.032,0,0.048,0c10.7-0.026,19.18-8.721,19.155-19.42c0-0.004,0-142.338,0-142.338c0-5.523,7.949-5.234,7.949,0v3.904
h0.001h15.24v-49.558H203.644z"/>
</g>
</g>
<g>
<g>
<path d="M237.08,117.277v49.558h37.784v-49.558C266.058,117.277,257.069,117.277,237.08,117.277z"/>
</g>
</g>
<g>
<g>
<path d="M308.299,117.277h-16.262v49.558h15.241v-3.898c0-5.285,8.064-5.504,8.006,0.101v142.231
c-0.026,10.699,8.398,19.394,19.098,19.42c0.016,0,0.031,0,0.048,0c10.676-0.001,19.575-8.644,19.601-19.327V163.036
C354.03,138.024,333.651,117.277,308.299,117.277z"/>
</g>
</g>
<g>
<g>
<path d="M307.279,184.009H204.664l0.009,304.743c0,12.839,10.408,23.247,23.247,23.247c12.839,0,23.247-10.408,23.247-23.247
v-185.82h10.038v185.82c0,12.839,10.408,23.247,23.247,23.247c12.839,0,23.247-10.408,23.247-23.247
C307.7,275.444,307.279,237.237,307.279,184.009z M272.788,253.617h-33.634c-6.323,0-11.45-5.126-11.45-11.45V208.84
c0-1.513,1.227-2.74,2.74-2.74h51.053c1.513,0,2.74,1.227,2.74,2.74v33.328h0C284.238,248.49,279.112,253.617,272.788,253.617z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

82
assets/hoplite.svg Normal file
View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path d="M279.632,331.013V487.6c0,13.476,10.923,24.4,24.4,24.4s24.4-10.924,24.4-24.4V359.974
C309.412,355.221,292.549,344.971,279.632,331.013z"/>
</g>
</g>
<g>
<g>
<path d="M363.372,136.481c-4.182-21.952-23.437-38.045-45.783-38.307c-39.957-0.47-47.914-0.507-91.123,0
c-17.957,0.211-34.496,10.879-42.135,27.131l-26.454,56.279l-40.179-9.891V84.199h6.738c3.154,0,5.446-3.006,4.615-6.047
l-16.82-61.537c-1.278-4.677-7.95-4.684-9.231,0l-16.82,61.537c-0.832,3.044,1.463,6.047,4.615,6.047h6.738v82.83
c-9.76-0.78-18.926,5.573-21.342,15.388c-2.684,10.904,3.98,21.919,14.884,24.604l6.458,1.59v293.309
c0,5.568,4.513,10.081,10.081,10.081s10.081-4.513,10.081-10.081V213.573l46.635,11.48c9.421,2.318,19.147-2.339,23.262-11.094
l32.702-70.515c0,189.055,0,99.771,0,344.156c0,13.476,10.924,24.4,24.4,24.4c13.476,0,24.4-10.924,24.4-24.4V317.465
c-10.363-15.997-15.898-34.6-15.898-53.837c0-54.762,44.559-99.302,99.302-99.302c5.6,0,11.091,0.474,16.44,1.368L363.372,136.481
z"/>
</g>
</g>
<g>
<g>
<circle cx="274.365" cy="42.141" r="42.141"/>
</g>
</g>
<g>
<g>
<path d="M378.403,270.181L378.403,270.181c-2.308,9.15-9.335,16.417-18.342,19.071v58.201
c40.788-3.633,73.185-36.347,76.339-77.272H378.403z"/>
</g>
</g>
<g>
<g>
<path d="M326.596,270.181h-57.998c3.154,40.924,35.552,73.638,76.34,77.272v-58.201
C335.932,286.597,328.904,279.331,326.596,270.181z"/>
</g>
</g>
<g>
<g>
<path d="M360.062,179.803v58.201h-0.001c8.333,2.456,14.968,8.863,17.742,17.055h58.445
C432.205,215.077,400.179,183.377,360.062,179.803z"/>
</g>
</g>
<g>
<g>
<path d="M268.751,255.059h58.445c2.773-8.192,9.409-14.599,17.742-17.055v-58.201
C304.821,183.377,272.794,215.077,268.751,255.059z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

53
assets/knight.svg Normal file
View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 496 496" style="enable-background:new 0 0 496 496;" xml:space="preserve">
<g>
<path d="M76,312c0,17.648,14.352,32,32,32s32-14.352,32-32v-32H76V312z M92,296h32v16c0,8.824-7.176,16-16,16
c-8.824,0-16-7.176-16-16V296z"/>
<path d="M266.656,220.44l-13.272-8.928c-0.032,0.04-2.6,3.712-8.088,7.888l9.688,12.736
C262.624,226.32,266.264,221.024,266.656,220.44z"/>
<path d="M204,248c14.272,0,27.32-2.824,38.784-8.392l-7-14.392C226.52,229.72,215.832,232,204,232
c-34.832,0-48.808-19.656-49.384-20.496l-13.272,8.928C142.096,221.56,160.216,248,204,248z"/>
<path d="M452,192h-43.312L344,256.688l-1.376-1.376c-4.648-4.648-11.08-7.312-17.656-7.312c-2.528,0-4.912,0.488-7.216,1.192
l-18.04-41.232c-6.368-14.552-20.76-23.96-36.64-23.96H249.16c10.368-8.376,18.32-19.568,22.792-32.4
C287.72,149.632,300,136.296,300,120V96c0-52.936-43.064-96-96-96c-52.936,0-96,43.064-96,96v24
c0,16.296,12.28,29.632,28.056,31.6c4.472,12.832,12.424,24.024,22.792,32.4h-15.072c-15.24,0-28.952,8.472-35.776,22.112
l-14.2,28.392c-1.552,0.544-3.064,1.184-4.536,1.92l-32,16C49.088,256.512,44,264.736,44,273.888V312c0,35.288,28.712,64,64,64
c5.576,0,10.96-0.792,16.128-2.136l-9.864,19.72l4.08,4.08c1.144,1.152,8.536,7.752,29.024,12.712l-6.448,32.232l-20.296,10.152
C112.832,456.64,108,464.464,108,473.168C108,485.76,118.24,496,130.832,496h36.048c11.408,0,21.304-8.12,23.528-19.296
l12.144-60.728c0.504,0,0.944,0.024,1.448,0.024c0.496,0,0.944-0.024,1.44-0.024l12.144,60.728
C219.816,487.88,229.712,496,241.12,496h36.048C289.76,496,300,485.76,300,473.168c0-8.704-4.832-16.528-12.624-20.424
l-20.296-10.152l-6.336-31.688c24.392-4.952,35.024-11.8,35.696-12.248l5.4-3.6l-19.936-49.84L308,371.312l11.688-11.688
c14.672-1.712,26.232-13.272,27.944-27.944l0.368-0.368l5.376,5.376c4.648,4.648,11.08,7.312,17.656,7.312
C384.8,344,396,332.8,396,319.032c0-6.672-2.592-12.944-7.312-17.656L387.312,300L452,235.312V192z M275.696,133.936
c0.168-1.96,0.304-3.928,0.304-5.936v-21.776c4.76,2.776,8,7.88,8,13.776C284,126.016,280.624,131.208,275.696,133.936z M124,120
c0-5.896,3.24-11,8-13.776V128c0,2.008,0.136,3.976,0.304,5.936C127.376,131.208,124,126.016,124,120z M133.24,88.752
c-3.248,0.704-6.256,1.992-9.056,3.6C126.112,49.936,161.12,16,204,16s77.888,33.936,79.816,76.352
c-2.8-1.608-5.808-2.896-9.056-3.6C271.664,79.072,262.688,72,252,72c-3.024,0-5.856-1.176-8-3.312l-16-16l-16,16
c-2.144,2.136-4.976,3.312-8,3.312h-48C145.312,72,136.336,79.072,133.24,88.752z M148,128V96c0-4.408,3.584-8,8-8h48
c7.288,0,14.152-2.84,19.312-8L228,75.312L232.688,80c5.168,5.16,12.024,8,19.312,8c4.416,0,8,3.592,8,8v32
c0,30.88-25.128,56-56,56S148,158.88,148,128z M122.312,213.264C126.408,205.08,134.624,200,143.776,200H180v-4.2
c7.52,2.672,15.576,4.2,24,4.2s16.48-1.528,24-4.2v4.2h35.072c9.528,0,18.168,5.648,21.992,14.376l6.384,14.6
C281.776,237.464,248.008,264,204,264c-12.816,0-25.872-2.336-38.824-6.824c-1.848-1.888-3.984-3.528-6.44-4.752l-32-16
c-4.368-2.176-9.12-3.536-13.968-4.072L122.312,213.264z M268,296l13.144,31.552l-4.504,4.504l-8.64-21.6V296z M180,304h-8
v-28.128c10.68,2.68,21.392,4.128,32,4.128c17.824,0,34.008-3.776,48-9.048V304h-24H180z M212,320v16h-16v-16H212z M108,360
c-26.472,0-48-21.528-48-48v-38.112c0-3.048,1.696-5.792,4.424-7.152l32-16C100,248.944,104,248,108,248s8,0.944,11.576,2.736
l32,16c2.728,1.36,4.424,4.104,4.424,7.152V312C156,338.472,134.472,360,108,360z M174.728,473.568
c-0.744,3.728-4.04,6.432-7.848,6.432h-36.048c-3.76,0-6.832-3.064-6.832-6.832c0-2.608,1.448-4.952,3.784-6.112l27.296-13.648
L157.76,440h23.68L174.728,473.568z M184.64,424h-23.68l2.128-10.632c6.736,0.976,14.528,1.704,23.256,2.16L184.64,424z
M223.36,424l-1.696-8.472c8.552-0.424,16.344-1.112,23.28-2.016L247.04,424H223.36z M280.216,467.056
c2.336,1.16,3.784,3.504,3.784,6.112c0,3.768-3.072,6.832-6.832,6.832H241.12c-3.808,0-7.104-2.704-7.84-6.432L226.56,440h23.68
l2.68,13.408L280.216,467.056z M204,400c-41.32,0-61.424-6.872-69.616-10.864l13.632-27.264
c12.632-10.16,21.312-25,23.432-41.872H180v32h48v-32h26.584l27.248,68.12C271.656,392.384,247.416,400,204,400z
M276.496,274.808l-5.256-12.624c11.832-6.352,21.008-13.064,26.904-17.896l6.336,14.472c-0.624,0.904-1.12,1.888-1.632,2.872
L276.496,274.808z M282.672,289.608l18.552-9.272c1.184,3.832,3.176,7.376,6.088,10.288l5.376,5.376l-19.312,19.312
L282.672,289.608z M308,348.688L295.312,336L324,307.312L336.688,320L308,348.688z M371.032,328c-2.36,0-4.672-0.96-6.344-2.624
l-46.064-46.064c-1.696-1.696-2.624-3.944-2.624-6.344c0-4.944,4.024-8.968,8.968-8.968c2.36,0,4.672,0.96,6.344,2.624
l46.064,46.064c1.696,1.696,2.624,3.944,2.624,6.344C380,323.976,375.976,328,371.032,328z M436,228.688l-60,60L355.312,268
l60-60H436V228.688z"/>
<rect x="228" y="104" width="16" height="16"/>
<rect x="164" y="104" width="16" height="16"/>
<rect x="196" y="152" width="16" height="16"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

1
assets/servant.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.9 KiB

1
assets/tower_big.svg Normal file
View File

@ -0,0 +1 @@
<svg height="464pt" viewBox="-136 0 464 464" width="464pt" xmlns="http://www.w3.org/2000/svg"><path d="m192 208h-192l31.777344 73.121094-24.945313 182.878906h178.328125l-24.9375-182.878906zm-112 131.3125 16-16 16 16v25.375l-16-16-16 16zm0 48 16-16 16 16v25.375l-16-16-16 16zm32-70.625-16-16-16 16v-28.6875h32zm-48-28.6875v44.6875l-15.871094-15.878906-5.832031 5.574218 4.6875-34.382812zm-25.175781 59.847656 9.054687-8.664062 16.121094 16.128906v25.375l-15.960938-15.960938-13.304687 13.105469zm-7.632813 55.9375 16.769532-16.511718 16.039062 16.039062v25.375l-15.335938-15.335938-21 16.3125zm16.144532 30.863282 13.351562 13.351562h-30.542969zm32.664062.664062 16-16 16 16v12.6875h-32zm63.335938-21.960938-15.335938 15.335938v-25.375l16.039062-16.039062 16.769532 16.511718 3.527344 25.871094zm18.519531 34.648438h-30.542969l13.351562-13.351562zm-17.894531-83.273438-15.960938 15.960938v-25.375l16.128906-16.121094 9.054688 8.664063 4.089844 29.984375zm-.089844-47.917968-15.871094 15.878906v-44.6875h17.015625l4.6875 34.382812zm-23.871094-76.808594h16v16h-16zm-32 0h16v16h-16zm-32 0h16v16h-16zm0 0"/><path d="m168 144h-16v-64h-16v-32h-32v-48h-16v48h-32v32h-16v64h-16v48h144zm-64-48v16h-16v-16zm32 16h-16v-16h16zm-32 16v16h-16v-16zm16 0h16v16h-16zm-48-32v16h-16v-16zm-16 32h16v16h-16zm0 0"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

54
assets/tower_small.svg Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 440.96 440.96" style="enable-background:new 0 0 440.96 440.96;" xml:space="preserve">
<g>
<g>
<path d="M350.32,367.92c2.065-3.953,3.162-8.34,3.2-12.8c0-15.773-12.787-28.56-28.56-28.56h-9.92l-36.8-153.92
c15.661-0.859,27.661-14.251,26.802-29.912c-0.69-12.578-9.582-23.198-21.842-26.088l32-43.68c0.247-0.381,0.461-0.783,0.64-1.2
V71.2c0.354-0.812,0.57-1.677,0.64-2.56V8c0-4.418-3.582-8-8-8H278c-4.418,0-8,3.582-8,8v16h-21.12V8c0-4.418-3.582-8-8-8h-36.4
c-4.418,0-8,3.582-8,8v16h-20.96V8c0-4.418-3.582-8-8-8H136.8c-4.418,0-8,3.582-8,8v60.16c0.027,0.932,0.216,1.853,0.56,2.72v0.56
c0.179,0.417,0.393,0.819,0.64,1.2l32,43.68c-15.267,3.598-24.726,18.891-21.128,34.158c2.89,12.26,13.51,21.153,26.088,21.842
l-36.8,154.08h-9.6c-15.773,0-28.56,12.787-28.56,28.56c0.026,4.366,1.067,8.666,3.04,12.56h-1.6c-8.837,0-16,7.163-16,16v41.44
c0,8.837,7.163,16,16,16h254.08c8.837,0,16-7.163,16-16v-41.2C363.562,375.971,357.989,369.283,350.32,367.92z M144.8,15.92h14.72
v16c0,4.418,3.582,8,8,8h36.96c4.418,0,8-3.582,8-8v-16h20.24v16c0,4.418,3.582,8,8,8h37.04c4.418,0,8-3.582,8-8v-16h14.72v44.16
H144.8V15.92z M152.8,76.08h140.16l-28.88,39.44h-82.64L152.8,76.08z M156.482,144.323c-0.001-0.054-0.002-0.108-0.002-0.163
l0.48-0.08c0-6.937,5.623-12.56,12.56-12.56h106.96c6.937-0.005,12.564,5.615,12.569,12.551
c0.005,6.937-5.615,12.564-12.551,12.569c-0.006,0-0.012,0-0.018,0h-107.2C162.344,156.773,156.615,151.258,156.482,144.323z
M183.68,172.64h78.16l36.72,153.92h-152L183.68,172.64z M118.557,342.56c0.667-0.053,1.336-0.053,2.003,0h204.4
c6.915-0.553,12.968,4.604,13.522,11.518c0.553,6.915-4.604,12.968-11.518,13.522c-0.667,0.053-1.336,0.053-2.003,0h-204.4
c-6.915,0.553-12.968-4.604-13.522-11.518C106.485,349.167,111.642,343.113,118.557,342.56z M347.6,425.2H93.36v-41.52H347.6
V425.2z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

48
assets/townhall.svg Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 492 492" style="enable-background:new 0 0 492 492;" xml:space="preserve">
<g>
<g>
<path style="fill:#010002;" d="M105,209.5v2c0,9.333,3.667,14,11,14h28v164h41v-143h41v143h40v-143h41v143h41v-164h28
c3.333,0,6.333-1.5,9-4.5s4-6.167,4-9.5c0-4.667-1.667-8.333-5-11v-1h1l-129-67v-19c6.667,3.333,17.333,3.833,32,1.5
s28-1.5,40,2.5v-43c-10.667-4-24-4.667-40-2s-26.667,2-32-2v-2c0-4.667-3.333-7-10-7s-10,2.333-10,7v64l-129,67h1h2h3
C107,200.833,104.333,204.167,105,209.5z"/>
<polygon style="fill:#010002;" points="133,399.5 103,430.5 389,430.5 359,399.5 "/>
<path style="fill:#010002;" d="M359,236.5v153h9l4,6l27,25l10,10h83v-194H359z M420,358.5h-31v-30h31V358.5z M420,297.5h-31v-31
h31V297.5z M461,358.5h-20v-30h20V358.5z M461,297.5h-20v-31h20V297.5z"/>
<path style="fill:#010002;" d="M124,389.5h9v-153H0v194h83l10-9l27-26L124,389.5z M51,358.5H31v-30h20V358.5z M51,297.5H31v-31h20
V297.5z M103,358.5H72v-30h31V358.5z M103,297.5H72v-31h31V297.5z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

104
assets/tree.svg Normal file
View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path d="M309.07,363.622l-10.14-61.019h2.639c2.23,0,4.352-0.982,5.797-2.688c1.445-1.704,2.066-3.958,1.7-6.163l-10.14-61.02
h2.636c2.235,0,4.357-0.982,5.801-2.688c1.445-1.704,2.066-3.959,1.7-6.163l-4.405-26.496c-0.008-0.053-0.016-0.104-0.024-0.156
l-1.642-9.895c-0.688-4.144-4.607-6.946-8.747-6.257c-4.143,0.687-6.944,4.604-6.256,8.747l1.618,9.749
c0.008,0.053,0.016,0.105,0.024,0.158l2.958,17.793H219.41l4.602-27.701c0.688-4.143-2.113-8.058-6.256-8.747
c-4.139-0.688-8.058,2.113-8.747,6.257l-6.073,36.548c-0.366,2.204,0.255,4.458,1.7,6.163c1.445,1.704,3.567,2.688,5.801,2.688
h2.636l-10.14,61.02c-0.366,2.204,0.255,4.458,1.7,6.163c1.445,1.704,3.567,2.688,5.801,2.688h2.638l-10.139,61.019
c-0.366,2.204,0.255,4.458,1.7,6.163s3.567,2.688,5.801,2.688h2.64l-6.387,38.458c-0.688,4.143,2.112,8.059,6.256,8.747
c0.422,0.07,0.84,0.104,1.254,0.104c3.652,0,6.874-2.639,7.492-6.359l6.801-40.949h55.019l6.801,40.949
c0.617,3.721,3.841,6.359,7.492,6.359c0.414,0,0.832-0.033,1.254-0.104c4.144-0.688,6.944-4.605,6.255-8.747l-6.387-38.458h2.64
c2.235,0,4.357-0.982,5.801-2.688S309.436,365.828,309.07,363.622z M219.406,357.264l9.083-54.661h55.021l9.083,54.661H219.406z
M219.406,287.395l9.083-54.662h55.021l9.083,54.662H219.406z"/>
</g>
</g>
<g>
<g>
<path d="M465.202,498.799c-38.997-36.702-97.039-61.282-163.434-69.211c-15.006-1.799-30.405-2.71-45.769-2.71
c-83.138,0-161.344,26.887-209.2,71.921c-3.059,2.878-3.205,7.69-0.326,10.749c2.877,3.059,7.69,3.203,10.749,0.326
c45.107-42.447,119.417-67.789,198.779-67.789c14.762,0,29.55,0.876,43.961,2.603c62.275,7.438,118.704,31.197,154.816,65.185
c1.468,1.382,3.342,2.067,5.211,2.067c2.023,0,4.041-0.803,5.538-2.393C468.406,506.49,468.259,501.677,465.202,498.799z"/>
</g>
</g>
<g>
<g>
<path d="M252.83,77.193c-20.867-40.39-68.374-66.488-121.028-66.488C59.125,10.705,0,60.167,0,120.963
c0,4.199,3.404,7.604,7.604,7.604h75.379c4.199,0,7.604-3.405,7.604-7.604c0-4.199-3.404-7.604-7.604-7.604H15.577
C20.344,64.49,70.65,25.913,131.802,25.913c47.028,0,89.231,22.869,107.517,58.261c1.926,3.731,6.516,5.193,10.245,3.266
C253.296,85.512,254.757,80.924,252.83,77.193z"/>
</g>
</g>
<g>
<g>
<path d="M439.803,22.581c-18.572-7.88-38.627-11.875-59.605-11.875c-52.857,0-100.433,26.235-121.208,66.836
c-1.913,3.738-0.433,8.32,3.306,10.233c1.109,0.568,2.291,0.836,3.457,0.836c2.764,0,5.429-1.512,6.776-4.142
c18.201-35.571,60.463-58.556,107.669-58.556c18.926,0,36.983,3.589,53.667,10.667c3.866,1.64,8.33-0.163,9.969-4.03
C445.474,28.684,443.67,24.22,439.803,22.581z"/>
</g>
</g>
<g>
<g>
<path d="M460.792,33.682c-3.527-2.281-8.234-1.27-10.514,2.256c-2.28,3.526-1.27,8.234,2.257,10.515
c25.666,16.598,41.343,40.662,43.89,66.907h-67.408c-4.2,0-7.604,3.405-7.604,7.604s3.404,7.604,7.604,7.604h75.379
c4.2,0,7.604-3.405,7.604-7.604C512,86.539,493.336,54.726,460.792,33.682z"/>
</g>
</g>
<g>
<g>
<path d="M304.01,9.973c-15.183-6.579-31.335-9.914-48.009-9.914c-16.089,0-31.713,3.109-46.439,9.241
c-3.877,1.614-5.711,6.066-4.097,9.943c1.614,3.877,6.067,5.711,9.942,4.097c12.864-5.357,26.522-8.073,40.593-8.073
c14.581,0,28.7,2.914,41.964,8.66c0.985,0.427,2.009,0.629,3.019,0.629c2.939,0,5.738-1.714,6.981-4.583
C309.633,16.12,307.863,11.643,304.01,9.973z"/>
</g>
</g>
<g>
<g>
<path d="M421.779,130.822c-24.706-24.705-55.963-39.988-88.016-43.033c-29.914-2.841-57.23,5.244-77.77,22.833
c-43.781-37.391-116.465-29.111-165.778,20.2c-17.571,17.571-30.579,38.789-37.619,61.359c-1.25,4.009,0.986,8.273,4.995,9.523
c4.016,1.251,8.272-0.986,9.524-4.995c6.31-20.231,18.017-39.295,33.856-55.134c43.238-43.24,106.087-51.537,144.013-20.353
l-164.37,164.37c-14.501-17.602-20.922-40.984-18.15-66.694c0.449-4.175-2.57-7.925-6.745-8.375
c-4.189-0.452-7.924,2.57-8.375,6.745c-3.581,33.226,6.233,63.311,27.637,84.712c1.484,1.484,3.431,2.227,5.376,2.227
c1.946,0,3.892-0.742,5.378-2.227l170.266-170.266L368.183,243.9c2.083,2.083,5.182,2.776,7.953,1.777l22.48-8.093l-7.701,23.101
c-0.912,2.733-0.202,5.745,1.835,7.782l33.513,33.512c1.427,1.425,3.36,2.227,5.378,2.227s3.95-0.801,5.378-2.227
C480.005,258.992,473.168,182.21,421.779,130.822z M431.379,285.588l-24.552-24.552l11.171-33.51
c0.921-2.764,0.182-5.81-1.901-7.845c-2.086-2.035-5.149-2.701-7.888-1.714l-32.686,11.768L267.011,121.22
c17.266-14.214,40.161-20.682,65.314-18.292c28.54,2.711,56.49,16.436,78.7,38.646
C454.265,184.815,462.562,247.663,431.379,285.588z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Antiyoy</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="synaptic.min.js"></script>
<script src="NeuralNetwork.js"></script>
<script src="GameElement.js"></script>
<script src="Civilization.js"></script>
<script src="CivilizationAI.js"></script>
<script src="Tile.js"></script>
<script src="Game.js"></script>
<script src="Warrior.js"></script>
<script src="main.js"></script>
</body>
</html>

28
index.js Normal file
View File

@ -0,0 +1,28 @@
const brain = require('brain.js');
const net = new brain.NeuralNetwork();
const out = net.train([
{
input:
{x: 0, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
// {x: 0.25, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
// {x: 0.75, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
// {x: 1, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
// ], output: 0
output: {x: 0, y: 0.5}
}
]);
console.log(out);
console.log('test');
console.log(net.toJSON());
const test = net.run([
{x: 0, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
{x: 0.25, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
{x: 0.75, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
{x: 1, y: 0.5, isFriendly: 1, isAccessible: 1, danger: 0, canAttack: 0, rank: .5, isTree: 0},
]);
console.log(test);

196
main.js Normal file
View File

@ -0,0 +1,196 @@
const body = document.querySelector('body');
let prevMouseEvent;
let timeout;
Node.prototype.createChild = function(tagName, classNames = null, text = null, src = null) {
const element = document.createElement(tagName);
if(!!classNames)
if(classNames instanceof Array)
element.classList.add(...classNames);
else
element.classList.add(classNames);
if(!!text)
element.innerText = text;
if(!!src)
element.src = src;
this.appendChild(element);
return element;
};
function createExpandBtn(parent, items) {
const container = parent.createChild('div', ['expandBtnContainer', 'expandBtnContainer' + items.length, 'disabled']);
const expandBtn = container.createChild('div', 'expandBtn');
expandBtn.addEventListener('click', () => {
container.classList.toggle('isActive');
}, false);
expandBtn.createChild('img', null, null, 'assets/' + items[0]);
const btns = [];
items.forEach((item, index) => {
const btn = container.createChild('div', 'btn' + (index + 1));
btn.createChild('img', null, null, 'assets/' + item);
btns.push(btn);
});
return [container, ...btns];
}
function createTroopsBtn(parent) {
const btn = createExpandBtn(parent, ['farmer.svg', 'hoplite.svg', 'servant.svg', 'knight.svg']);
btn[0].classList.add('btn-troops');
return btn;
}
function createTowersBtn(parent) {
const btn = createExpandBtn(parent, ['farm.svg', 'tower_small.svg', 'tower_big.svg']);
btn[0].classList.add('btn-buildings');
return btn;
}
class Building extends GameElement {
constructor(x, y, game, civilization, asset) {
super(x, y, game, asset);
this.civilization = civilization;
this.rank = 0;
}
adjustPosition() {
const tile = this.game.field[this.x][this.y].element;
this.displayWidth = Math.round(this.game.hexagonSize.width * .9);
let offsetX = tile.offsetLeft;
let offsetY = tile.offsetTop;
if (this.y % 2 !== 0)
offsetX += Math.round(this.displayWidth * .5);
this.element.style.width = this.displayWidth + 'px';
this.element.style.left = offsetX + 'px';
this.element.style.top = offsetY + 'px';
}
destroy() {
this.game.field[this.x][this.y].object = null;
this.element.parentNode.removeChild(this.element);
}
}
class Farm extends Building {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, 'farm.svg');
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'farm', 'farm.svg');
}
static get initialCosts() {
return 10; // TODO: dynamic price
}
}
class Tower extends Building {
constructor(x, y, game, civilization, asset) {
super(x, y, game, civilization, asset);
}
}
class SmallTower extends Tower {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, 'tower_small.svg');
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'towerSmall', 'tower_small.svg');
}
static get initialCosts() {
return 20;
}
}
class BigTower extends Tower {
constructor(x, y, game, civilization) {
super(x, y, game, civilization, 'tower_big.svg');
}
static getDraggingObject(game) {
return super.getDraggingObject(game, 'towerBig', 'tower_big.svg');
}
static get initialCosts() {
return 30;
}
}
class Townhall extends Building {
constructor(civilization, game) {
super(civilization.origin.x, civilization.origin.y, game, civilization, 'townhall.svg');
}
destroy() {
super.destroy();
// TODO: New townhall
console.log('hallo');
this.civilization.townhall = null;
this.civilization.split();
}
}
class Tree extends GameElement {
constructor(x, y, game) {
super(x, y, game, 'tree.svg');
}
breed() {
if(Math.random() < .25) {
const tiles = this.game.field[this.x][this.y].getAvailableAdjacentTiles();
if(tiles.length === 0)
return;
const index = Math.floor(Math.random() * (tiles.length - 1));
const {x, y} = tiles[index].pos;
this.game.addTree(x, y);
}
}
cutDown() {
delete this.game.trees[this.uuid];
this.element.parentNode.removeChild(this.element);
}
}
class Player {
constructor(color) {
this.color = color;
this.uuid = guid();
}
}
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
const player = new Player('red');
// let game = new GameNN();
let game = new Game();
const civilization = game.addCivilization(player, 2, 4, true);
// const anotherOne = new Player('yellow');
// const another = game.addCivilization(anotherOne, 5, 3);
// game.addWarrior(1, 4, civilization, Farmer);
// game.addWarrior(4, 2, another, Farmer);
//
// game.addTree(1,4);
// game.addTree(1,5);

40
package-lock.json generated Normal file
View File

@ -0,0 +1,40 @@
{
"name": "antiyoy",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"acorn": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
},
"brain.js": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/brain.js/-/brain.js-1.6.0.tgz",
"integrity": "sha512-HMHK7ueavtlONnpa+qDDJNs4K5gegGxQkKir+TeBoXgyHjI84y4vUh44q9qOOv2LwtVlZf1h5gEp676OWnkCbA==",
"requires": {
"gpu.js": "^1.10.4",
"thaw.js": "^2.0.0"
}
},
"gpu.js": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/gpu.js/-/gpu.js-1.10.4.tgz",
"integrity": "sha512-qh5O1LXwwkhGOpEicR9SnQB7xduUDY5rKGPWj9yOFLNI90bX+mReooGQjnL3iZ08btBOT0zQGu9wKA+gGy2BhQ==",
"requires": {
"acorn": "^5.1.1"
}
},
"synaptic": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/synaptic/-/synaptic-1.1.4.tgz",
"integrity": "sha512-JFtU98VDFI0uBTowgySo+UUEkc7rSVHaxx9D6dR5BK6u81G7uAxcp32Gi2SHciLpFbvJVd1Pfz7aqSNQO4EH9Q=="
},
"thaw.js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/thaw.js/-/thaw.js-2.0.0.tgz",
"integrity": "sha1-RSvF1+4s4bb5IDewW1BsvWVikdA="
}
}
}

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "antiyoy",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"brain.js": "^1.6.0",
"synaptic": "^1.1.4"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

389
style.css Normal file
View File

@ -0,0 +1,389 @@
body {
background-color: #171d1c;
margin: 0;
padding: 0;
overflow: hidden;
font-family: Roboto, Arial, sans-serif;
}
.clear:after {
content: "";
display: block;
clear: both;
}
.grey {
background-color: #9a9a9a;
}
.red {
background-color: #f93943;
}
.yellow {
background-color: #fde74c;
}
.blue {
background-color: #5fbff9;
}
.green {
background-color: #45cb85;
}
.purple {
background-color: #5c2751;
}
.text-grey {
color: #9a9a9a;
}
.gameContainer {
position: relative;
width: 80%;
margin: 0 auto;
padding: 0;
height: 100vh;
}
@media (max-width: 500px) {
.gameContainer {
width: 100%;
}
}
.viewContainer {
position: relative;
overflow: hidden;
width: 100%;
height: calc(100vh - 2 * 84px);
}
.view {
position: absolute;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
width: 100%;
transition: transform .2s;
}
.view.zooming {
transition: transform .2s, left .2s, top .2s;
}
.playground {
position: relative;
margin: 0 auto;
padding: 0;
}
.playground > li {
list-style-type: none;
position: relative;
float: left;
width: 27.85714285714286%;
padding: 0 0 32.16760145166612% 0;
-o-transform: rotate(-60deg) skewY(30deg);
-moz-transform: rotate(-60deg) skewY(30deg);
-webkit-transform: rotate(-60deg) skewY(30deg);
-ms-transform: rotate(-60deg) skewY(30deg);
transform: rotate(-60deg) skewY(30deg);
overflow: hidden;
visibility: hidden;
margin-right: .33%;
}
.playground > li.even-row {
-o-transform: translateX(50%) rotate(-60deg) skewY(30deg);
-moz-transform: translateX(50%) rotate(-60deg) skewY(30deg);
-webkit-transform: translateX(50%) rotate(-60deg) skewY(30deg);
-ms-transform: translateX(50%) rotate(-60deg) skewY(30deg);
transform: translateX(50%) rotate(-60deg) skewY(30deg);
}
.playground > li .hexagon {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-o-transform: skewY(-30deg) rotate(60deg);
-moz-transform: skewY(-30deg) rotate(60deg);
-webkit-transform: skewY(-30deg) rotate(60deg);
-ms-transform: skewY(-30deg) rotate(60deg);
transform: skewY(-30deg) rotate(60deg);
visibility: visible;
overflow: hidden;
}
.gameContainer .gameElement {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
.gameContainer .townhall {
transform: translate(6%, 8%);
}
.gameContainer .tree {
transform: translate(8%, 12%) scale(.8);
}
.gameContainer .farmer {
transform: translate(5%, 12%);
transition: top .5s, left .5s, transform .5s;
}
.gameContainer .hoplite {
transform: translate(12%, 10%);
transition: top .5s, left .5s, transform .5s;
}
.gameContainer .gameElement.waiting {
animation: waiting .7s infinite;
transition: margin-top .5s;
}
@keyframes waiting {
0% {
/*transform: translate(14%, 10%);*/
margin-top: 0;
}
60% {
/*transform: translate(14%, 0);*/
margin-top: -4px;
}
90% {
/*transform: translate(14%, 10%);*/
margin-top: 0;
}
100% {
margin-top: 0;
}
}
.draggingContainer {
position: absolute;
transform: translateX(-50%);
cursor: none;
pointer-events: none;
}
.draggingContainer > .dragging {
pointer-events: none;
-webkit-transform-origin: 50% 5%;
-moz-transform-origin: 50% 5%;
-ms-transform-origin: 50% 5%;
-o-transform-origin: 50% 5%;
transform-origin: 50% 5%;
transition: transform .25s, width .2s, height .2s;
}
.gameStats, .controls {
position: relative;
height: 64px;
padding: 10px 20px;
}
.gameStats > .gameStat {
position: absolute;
color: #fff;
font-weight: normal;
}
.gameStats > .gameStat.center {
left: 50%;
transform: translateX(-50%);
}
.gameStats > .gameStat.right {
right: 0;
}
.gameStats > .gameStat > .count {
font-weight: bold;
margin: 0 5px;
}
.btn {
font-size: 18px;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
transition: color .2s, background-color .2s, box-shadow .2s;
}
.btn-primary {
color: #fff;
background-color: #5c2751;
}
.btn-primary:hover {
color: #5c2751;
background: #fff;
-webkit-box-shadow: 0 0 10px #5c2751;
-moz-box-shadow: 0 0 10px #5c2751;
box-shadow: 0 0 10px #5c2751;
}
.btn-img {
padding: 8px;
width: 22px;
height: 22px;
background-color: #fff;
border-radius: 100%;
transform: scale(1.75);
transition: background-color .5s, opacity .5s, transform .5s cubic-bezier(.68, -0.55, .27, 1.55), box-shadow .2s;
}
.btn-img:hover {
-webkit-box-shadow: 0 0 10px #5c2751;
-moz-box-shadow: 0 0 10px #5c2751;
box-shadow: 0 0 10px #5c2751;
}
.btn-img.disabled {
background-color: #ccc;
opacity: .8;
box-shadow: none;
transform: none;
}
.btn-nextRound {
position: absolute;
left: 50%;
transform: translateX(-50%);
margin: 12px 0;
}
.expandBtnContainer {
position: absolute;
width: 64px;
height: 64px;
transition: transform .5s cubic-bezier(.68, -0.55, .27, 1.55);
}
.btn-troops {
left: 20px;
}
.btn-buildings {
right: 20px;
}
.expandBtnContainer.disabled {
transform: scale(.5);
pointer-events: none;
}
.expandBtnContainer > div,
.expandBtnContainer > div,
.expandBtnContainer > div,
.expandBtnContainer > div,
.expandBtnContainer > div{
width: 100%;
height: 100%;
background: #fff;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.expandBtnContainer .expandBtn {
z-index: 1;
}
.expandBtnContainer > div > img {
position: absolute;
height: 60%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.expandBtnContainer .expandBtn:before {
content: '';
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
position: absolute;
background: rgba(255, 72, 120, 0.5);
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.expandBtnContainer.isActive .expandBtn:before {
-webkit-transform: translate(-50%, -50%) scale(1.4);
transform: translate(-50%, -50%) scale(1.4);
}
.expandBtnContainer > div:not(.expandBtn),
.expandBtnContainer > div:not(.expandBtn),
.expandBtnContainer > div:not(.expandBtn),
.expandBtnContainer > div:not(.expandBtn) {
width: 75%;
height: 75%;
background: #19d1e7;
transition-delay: 0s;
}
.expandBtnContainer.expandBtnContainer4.isActive .btn1 {
top: 0;
left: -60%;
transition-delay: .1s;
}
.expandBtnContainer.expandBtnContainer4.isActive .btn2 {
top: -60%;
left: 12.5%;
transition-delay: .2s;
}
.expandBtnContainer.expandBtnContainer4.isActive .btn3 {
top: -60%;
left: 97.5%;
transition-delay: .3s;
}
.expandBtnContainer.expandBtnContainer4.isActive .btn4 {
top: 0;
left: 160%;
transition-delay: 0s;
}
.expandBtnContainer.expandBtnContainer3.isActive .btn1 {
top: -20%;
left: -50%;
transition-delay: .1s;
}
.expandBtnContainer.expandBtnContainer3.isActive .btn2 {
top: -75%;
left: 50%;
transition-delay: .2s;
}
.expandBtnContainer.expandBtnContainer3.isActive .btn3 {
top: -20%;
left: 150%;
transition-delay: 0s;
}

3048
synaptic.min.js vendored Normal file

File diff suppressed because it is too large Load Diff