Merge branch 'tron' of github.com:gnieark/botsArena into tron
This commit is contained in:
commit
49ed343300
|
@ -1 +1 @@
|
||||||
1448
|
1617
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7c8b786228bb9e1561ff60a2d6f7f6ce91be6fee
|
Subproject commit 1d85f9ef3ecfc42bbc4f3c70d5e37ca9a65f629a
|
22
src/arenas/tron/Coords.php
Normal file
22
src/arenas/tron/Coords.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
class Coords{
|
||||||
|
|
||||||
|
public $x;
|
||||||
|
public $y;
|
||||||
|
|
||||||
|
public function __construct(int $x = 0, int $y = 0) {
|
||||||
|
$this->x = $x;
|
||||||
|
$this->y = $y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(){
|
||||||
|
return $this->x.",".$this->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addDirection(Direction $dir){
|
||||||
|
return new Coords(
|
||||||
|
$this->x + $dir->deltaX,
|
||||||
|
$this->y + $dir->deltaY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
92
src/arenas/tron/Direction.php
Normal file
92
src/arenas/tron/Direction.php
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
class InvalidDirectionException extends UnexpectedValueException{
|
||||||
|
|
||||||
|
}
|
||||||
|
class Direction
|
||||||
|
{
|
||||||
|
private static $top = 0;
|
||||||
|
private static $bottom = 1;
|
||||||
|
private static $left = 2;
|
||||||
|
private static $right = 3;
|
||||||
|
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
public $deltaX;
|
||||||
|
public $deltaY;
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
$this->value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setValue($value){
|
||||||
|
$this->value = $value;
|
||||||
|
switch ($value){
|
||||||
|
case Direction::$bottom:
|
||||||
|
$this->deltaY = -1;
|
||||||
|
$this->deltaX = 0;
|
||||||
|
break;
|
||||||
|
case Direction::$top:
|
||||||
|
$this->deltaY = 1;
|
||||||
|
$this->deltaX = 0;
|
||||||
|
break;
|
||||||
|
case Direction::$left:
|
||||||
|
$this->deltaY = 0;
|
||||||
|
$this->deltaX = -1;
|
||||||
|
break;
|
||||||
|
case Direction::$right:
|
||||||
|
$this->deltaY = 0;
|
||||||
|
$this->deltaX = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(){
|
||||||
|
switch ($this->value){
|
||||||
|
case Direction::$top:
|
||||||
|
return "y+";
|
||||||
|
break;
|
||||||
|
case Direction::$bottom:
|
||||||
|
return "y-";
|
||||||
|
break;
|
||||||
|
case Direction::$left:
|
||||||
|
return "x-";
|
||||||
|
break;
|
||||||
|
case Direction::$right:
|
||||||
|
return "x+";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function make($str){
|
||||||
|
$dir = new Direction();
|
||||||
|
switch((string)$str){
|
||||||
|
case "x+":
|
||||||
|
$dir->setValue(Direction::$right);
|
||||||
|
break;
|
||||||
|
case "x-":
|
||||||
|
$dir->setValue(Direction::$left);
|
||||||
|
break;
|
||||||
|
case "y+":
|
||||||
|
$dir->setValue(Direction::$top);
|
||||||
|
break;
|
||||||
|
case "y-":
|
||||||
|
$dir->setValue(Direction::$bottom);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidDirectionException("expected 'x+', 'x-', 'y+' or 'y-'". (string)$str."received.");
|
||||||
|
}
|
||||||
|
return $dir;
|
||||||
|
}
|
||||||
|
public function opposite(){
|
||||||
|
$opposites = array(
|
||||||
|
Direction::$top => Direction::$bottom,
|
||||||
|
Direction::$bottom => Direction::$top,
|
||||||
|
Direction::$left => Direction::$right,
|
||||||
|
Direction::$right => Direction::$left
|
||||||
|
);
|
||||||
|
|
||||||
|
$opposite = new Direction();
|
||||||
|
$opposite->setValue($opposites[$this->value]);
|
||||||
|
return $opposite;
|
||||||
|
}
|
||||||
|
}
|
59
src/arenas/tron/Trail.php
Normal file
59
src/arenas/tron/Trail.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
class AlreadyBeenAddedException extends LogicException { }
|
||||||
|
|
||||||
|
class Trail {
|
||||||
|
private $trail;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->trail = new SplStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function last() {
|
||||||
|
return $this->trail->top();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function emptyTrail(){
|
||||||
|
$this->trail = new SplStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($value) {
|
||||||
|
if(!$this->trail->isEmpty()) {
|
||||||
|
if(Trail::kind($this->trail->bottom()) !== Trail::kind($value)) {
|
||||||
|
throw new TypeError(
|
||||||
|
'items added to a trail must be of the same kind'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->contains($value)) {
|
||||||
|
throw new AlreadyBeenAddedException(
|
||||||
|
'value has already been added to the trail'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->trail->push($value);
|
||||||
|
}
|
||||||
|
public function __toString(){
|
||||||
|
return json_encode($this->getTrailAsArray());
|
||||||
|
}
|
||||||
|
public function getTrailAsArray(){
|
||||||
|
$arr = array();
|
||||||
|
foreach($this->trail as $coord) {
|
||||||
|
$arr[] = array($coord->x,$coord->y);
|
||||||
|
}
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
public function contains($searchedValue) {
|
||||||
|
foreach($this->trail as $value) {
|
||||||
|
if($value == $searchedValue) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function kind($var) {
|
||||||
|
$type = gettype($var);
|
||||||
|
if($type == 'object') $type .= ' ' . get_class($var);
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
}
|
260
src/arenas/tron/TronGame.php
Normal file
260
src/arenas/tron/TronGame.php
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
<?php
|
||||||
|
class TronGame
|
||||||
|
{
|
||||||
|
private $bots; //array of bots
|
||||||
|
public $gameId;
|
||||||
|
private $status; //false => Game ended or not initialised
|
||||||
|
|
||||||
|
public function get_continue(){
|
||||||
|
//count bots alive. if less than 1, game is ended
|
||||||
|
$count = 0;
|
||||||
|
foreach($this->bots as $bot){
|
||||||
|
if( $bot->isAlive == true){
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($count > 1){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function apply_looses($loosersArr){
|
||||||
|
//save draws
|
||||||
|
if( count($loosersArr) > 1 ){
|
||||||
|
$loosersById = array();
|
||||||
|
foreach($loosersArr as $bot){
|
||||||
|
$loosersById[] = $this->bots[$bot]->id;
|
||||||
|
}
|
||||||
|
$this->save_draw_bots($loosersById);
|
||||||
|
}
|
||||||
|
|
||||||
|
//save victories
|
||||||
|
if( count($loosersArr) > 0 ){
|
||||||
|
//make victorous bots array
|
||||||
|
$vbots = array();
|
||||||
|
for ($botCount = 0; $botCount < $nbeBots; $botCount++){
|
||||||
|
if($this->bots[$botCount]->isAlive){
|
||||||
|
$vbots[] = $this->bots[$botCount]->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->save_losers_winers($loosersById,$vbots);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private function save_draw_bots($arr){
|
||||||
|
/*
|
||||||
|
* Recursive function who save all combionaisons of draw matches
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(count($arr) < 2){
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
$a = $arr[0];
|
||||||
|
array_shift($arr);
|
||||||
|
foreach($arr as $bot){
|
||||||
|
save_battle('tron',$a,$bot,0,'id');
|
||||||
|
}
|
||||||
|
$this->save_draw_bots($arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function save_losers_winers($arrLoosers,$arrWiners){
|
||||||
|
foreach($arrWiners as $winner){
|
||||||
|
foreach($arrLoosers as $loser){
|
||||||
|
save_battle('tron',$winer,$loser,1,'id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_trails(){
|
||||||
|
//return all trails for draw svg
|
||||||
|
$trailsArr = array();
|
||||||
|
foreach($this->bots as $bot){
|
||||||
|
$trailsArr[] = $bot->trail->getTrailAsArray();
|
||||||
|
}
|
||||||
|
return $trailsArr;
|
||||||
|
}
|
||||||
|
public function new_lap(){
|
||||||
|
// for all alive bots
|
||||||
|
$logs = "";
|
||||||
|
$nbeBots = count($this->bots);
|
||||||
|
$urls = array();
|
||||||
|
$paramToSend = array();
|
||||||
|
$board = $this->get_trails();
|
||||||
|
$loosers = array();
|
||||||
|
$lastsCells = array();
|
||||||
|
|
||||||
|
for ($botCount = 0; $botCount < $nbeBots; $botCount++){
|
||||||
|
if ($this->bots[$botCount]->isAlive){
|
||||||
|
|
||||||
|
$urls[$botCount] = $this->bots[$botCount]->url;
|
||||||
|
|
||||||
|
$paramsToSend[$botCount] = array(
|
||||||
|
'game-id' => "".$this->gameId,
|
||||||
|
'action' => 'play-turn',
|
||||||
|
'game' => 'tron',
|
||||||
|
'board' => $board,
|
||||||
|
'player-index' => $botCount, // To do: verifier que ça restera le même à chaque tour
|
||||||
|
'players' => $nbeBots
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$responses = $this->get_multi_IAS_Responses($urls,$paramsToSend);
|
||||||
|
//print_r($responses);
|
||||||
|
//grow bots'tails
|
||||||
|
for ($botCount = 0; $botCount < $nbeBots; $botCount++){
|
||||||
|
if ($this->bots[$botCount]->isAlive){
|
||||||
|
|
||||||
|
$dir = Direction::make($responses[$botCount]['responseArr']['play']);
|
||||||
|
|
||||||
|
$lastsCells[$botCount] = $this->bots[$botCount]->grow($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test if loose
|
||||||
|
for ($botCount = 0; $botCount < $nbeBots; $botCount++){
|
||||||
|
if ($this->bots[$botCount]->isAlive){
|
||||||
|
for( $botCount2 = 0; $botCount2 < $nbeBots; $botCount2++){
|
||||||
|
|
||||||
|
if(($botCount <> $botCount2)
|
||||||
|
&& ($this->bots[$botCount2]->trail->contains($lastsCells[$botCount]))
|
||||||
|
){
|
||||||
|
$loosers[] = $botCount;
|
||||||
|
$this->bots[$botCount]->loose();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->get_trails();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function get_multi_IAS_Responses($iasUrls, $postParams){
|
||||||
|
//same as the get_IAS_Responses function
|
||||||
|
// but more than one bot requested parallely
|
||||||
|
|
||||||
|
$cmh = curl_multi_init();
|
||||||
|
for ($i = 0; $i < count($iasUrls); $i++){
|
||||||
|
$data_string = json_encode($postParams[$i]);
|
||||||
|
|
||||||
|
$ch[$i] = curl_init($iasUrls[$i]);
|
||||||
|
curl_setopt($ch[$i], CURLOPT_CUSTOMREQUEST, "POST");
|
||||||
|
curl_setopt($ch[$i], CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
curl_setopt($ch[$i], CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch[$i], CURLOPT_POSTFIELDS, $data_string);
|
||||||
|
curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch[$i], CURLOPT_HTTPHEADER, array(
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Content-Length: ' . strlen($data_string))
|
||||||
|
);
|
||||||
|
curl_multi_add_handle($cmh,$ch[$i]);
|
||||||
|
}
|
||||||
|
//send the requests
|
||||||
|
do {
|
||||||
|
$returnVal = curl_multi_exec($cmh, $runningHandles);
|
||||||
|
} while ($returnVal == CURLM_CALL_MULTI_PERFORM);
|
||||||
|
// Loop and continue processing the request
|
||||||
|
while ($runningHandles && $returnVal== CURLM_OK) {
|
||||||
|
// Wait forever for network
|
||||||
|
$numberReady = curl_multi_select($cmh);
|
||||||
|
if ($numberReady != -1) {
|
||||||
|
// Pull in any new data, or at least handle timeouts
|
||||||
|
do {
|
||||||
|
$returnVal = curl_multi_exec($cmh, $runningHandles);
|
||||||
|
} while ($returnVal == CURLM_CALL_MULTI_PERFORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get results
|
||||||
|
for ($i = 0; $i < count($iasUrls); $i++){
|
||||||
|
// Check for errors
|
||||||
|
$curlError = curl_error($ch[$i]);
|
||||||
|
if($curlError == "") {
|
||||||
|
$response = curl_multi_getcontent($ch[$i]);
|
||||||
|
if(! $arr = json_decode($response,TRUE)){
|
||||||
|
$arr=array();
|
||||||
|
}
|
||||||
|
$res[$i] = array(
|
||||||
|
'messageSend' => json_encode($postParams[$i]),
|
||||||
|
'response' => $response,
|
||||||
|
'httpStatus' => curl_getinfo($ch[$i])['http_code'],
|
||||||
|
'responseArr' => $arr
|
||||||
|
);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
$res[$i] = false;
|
||||||
|
}
|
||||||
|
//close
|
||||||
|
curl_multi_remove_handle($cmh, $ch[$i]);
|
||||||
|
curl_close($ch[$i]);
|
||||||
|
}
|
||||||
|
// Clean up the curl_multi handle
|
||||||
|
curl_multi_close($cmh);
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init_game(){
|
||||||
|
//send init messages to bots
|
||||||
|
$logs = "";
|
||||||
|
$fullLogs = "";
|
||||||
|
$nbeBots = count($this->bots);
|
||||||
|
for ($botCount = 0; $botCount < $nbeBots; $botCount++){
|
||||||
|
$messageArr = array(
|
||||||
|
'game-id' => "".$this->gameId,
|
||||||
|
'action' => 'init',
|
||||||
|
'game' => 'tron',
|
||||||
|
'board' => '',
|
||||||
|
'players' => $nbeBots,
|
||||||
|
'player-index' => $botCount
|
||||||
|
);
|
||||||
|
|
||||||
|
$resp = get_IA_Response($this->bots[$botCount]->url,$messageArr);
|
||||||
|
$fullLogs .= 'Arena send to '.$this->bots[$botCount]->name.'<em>'.htmlentities($resp['messageSend']).'</em><br/>
|
||||||
|
HTTP status: <em>'.htmlentities($resp['httpStatus']).'</em><br/>
|
||||||
|
Bot anwser: <em>'.htmlentities($resp['response']).'</em><br/>';
|
||||||
|
$logs.="Init message send to ".$this->bots[$botCount]->name."<br/>";
|
||||||
|
}
|
||||||
|
return array($logs,$fullLogs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($botsInfos){
|
||||||
|
$this->gameId = get_unique_id();
|
||||||
|
$this->bots = array();
|
||||||
|
$positions = array();
|
||||||
|
$botCount = 0;
|
||||||
|
$err = "";
|
||||||
|
|
||||||
|
//print_r($botsInfos);
|
||||||
|
|
||||||
|
foreach($botsInfos as $bot){
|
||||||
|
//find a random start position
|
||||||
|
do{
|
||||||
|
$x = rand(1,999);
|
||||||
|
$y = rand(1,999);
|
||||||
|
}while(in_array($x.",".$y,$positions));
|
||||||
|
|
||||||
|
$positions[] = $x.",".$y;
|
||||||
|
$startCoord = new Coords($x,$y);
|
||||||
|
|
||||||
|
$this->bots[$botCount] = new TronPlayer();
|
||||||
|
$this->bots[$botCount]->make($bot['id'],$startCoord,$bot['name'],$bot['url']);
|
||||||
|
|
||||||
|
if ($this->bots[$botCount]->isAlive === false){
|
||||||
|
$err .= "Something went wrong for ".$this->bots[$botCount]->getName()."<br/>";
|
||||||
|
}else{
|
||||||
|
$botCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
}
|
30
src/arenas/tron/TronPlayer.php
Normal file
30
src/arenas/tron/TronPlayer.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
class TronPlayer{
|
||||||
|
public $url;
|
||||||
|
public $id;
|
||||||
|
public $name;
|
||||||
|
public $trail;
|
||||||
|
private $direction;
|
||||||
|
public $isAlive = true;
|
||||||
|
|
||||||
|
public function grow(Direction $dir){
|
||||||
|
$this->trail->add($this->trail->last()->addDirection($dir));
|
||||||
|
return $this->trail->last();
|
||||||
|
}
|
||||||
|
public function loose(){
|
||||||
|
$this->isAlive = false;
|
||||||
|
$this->trail->emptyTrail();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public function make($botId, Coords $initialsCoords,$name,$url){
|
||||||
|
$this->id = $botId;
|
||||||
|
$this->trail = new Trail;
|
||||||
|
$this->trail->add($initialsCoords);
|
||||||
|
$this->name = $name;
|
||||||
|
$this->url = $url;
|
||||||
|
$this->state = true;
|
||||||
|
}
|
||||||
|
public function __construct(){
|
||||||
|
$this->state = false;
|
||||||
|
}
|
||||||
|
}
|
87
src/arenas/tron/act.php
Normal file
87
src/arenas/tron/act.php
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
#- BEGIN LICENSE BLOCK ---------------------------------------
|
||||||
|
#
|
||||||
|
# This file is part of botsArena.
|
||||||
|
#
|
||||||
|
# Copyright (C) Gnieark et contributeurs
|
||||||
|
# Licensed under the GPL version 3.0 license.
|
||||||
|
# See LICENSE file or
|
||||||
|
# http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
||||||
|
#
|
||||||
|
# -- END LICENSE BLOCK -----------------------------------------
|
||||||
|
|
||||||
|
require_once ("TronGame.php");
|
||||||
|
require_once ("TronPlayer.php");
|
||||||
|
require_once ("Direction.php");
|
||||||
|
require_once ("Trail.php");
|
||||||
|
require_once ("Coords.php");
|
||||||
|
|
||||||
|
switch ($_POST['act']){
|
||||||
|
case "initGame":
|
||||||
|
|
||||||
|
$rs = mysqli_query($lnMysql,"SELECT id,name,url FROM bots WHERE game='tron';");
|
||||||
|
while($r = mysqli_fetch_row($rs)){
|
||||||
|
$botsFullArr[$r[0]] = array('id' => $r[0], 'name' => $r[1], 'url' => $r[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$botsArrayTemp = json_decode($_POST['bots']);
|
||||||
|
$botsInfos = array();
|
||||||
|
|
||||||
|
foreach($botsArrayTemp as $id){
|
||||||
|
//tester si le bot existe dans la bdd
|
||||||
|
if(isset($botsFullArr[$id])){
|
||||||
|
$botsInfos[] = $botsFullArr[$id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//************
|
||||||
|
$game = new TronGame($botsInfos);
|
||||||
|
|
||||||
|
|
||||||
|
$logs = $game->init_game();
|
||||||
|
|
||||||
|
echo json_encode(array(
|
||||||
|
'status' => $game->get_continue(),
|
||||||
|
'logs' => $logs,
|
||||||
|
'gameId' => $game->gameId,
|
||||||
|
'botsPosition' => $game->get_trails()
|
||||||
|
));
|
||||||
|
|
||||||
|
$_SESSION['game'] = serialize($game);
|
||||||
|
|
||||||
|
|
||||||
|
die;
|
||||||
|
break;
|
||||||
|
case "play":
|
||||||
|
$logs = "";
|
||||||
|
if(!isset($_SESSION['game'])){
|
||||||
|
echo '{"status":"error"}';
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
|
$game = unserialize($_SESSION['game']);
|
||||||
|
|
||||||
|
if($game->gameId <> $_POST['gameId']){
|
||||||
|
//sometimes if an ajax callback is applied after init an other game
|
||||||
|
echo '{"status":"error"}';
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
$lap = $game->new_lap();
|
||||||
|
if($game->get_continue()){
|
||||||
|
$continue = 1;
|
||||||
|
}else{
|
||||||
|
$continue = 0;
|
||||||
|
}
|
||||||
|
echo json_encode(array(
|
||||||
|
'gameId' => $game->gameId,
|
||||||
|
'continue' => $continue,
|
||||||
|
'lap' => $lap
|
||||||
|
));
|
||||||
|
$_SESSION['game'] = serialize($game);
|
||||||
|
die;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
0
src/arenas/tron/doc-en.html
Normal file
0
src/arenas/tron/doc-en.html
Normal file
0
src/arenas/tron/doc-fr.html
Normal file
0
src/arenas/tron/doc-fr.html
Normal file
5
src/arenas/tron/functions.php
Normal file
5
src/arenas/tron/functions.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
167
src/arenas/tron/js.js
Normal file
167
src/arenas/tron/js.js
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
function createElem(type,attributes){
|
||||||
|
var elem=document.createElement(type);
|
||||||
|
for (var i in attributes)
|
||||||
|
{elem.setAttribute(i,attributes[i]);}
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
function addLog(message){
|
||||||
|
var divLogs = document.getElementById("logs");
|
||||||
|
var p=createElem('p',{});
|
||||||
|
p.innerHTML=message;
|
||||||
|
divLogs.appendChild(p);
|
||||||
|
divLogs.scrollTop = divLogs.scrollHeight;
|
||||||
|
|
||||||
|
}
|
||||||
|
function growTails(newPointsByPlayer){
|
||||||
|
|
||||||
|
var botsColor = ['cyan','darkmagenta','darkred','darkslategrey','deeppink','dodgerblue','goldenrod','grey','indigo','lightgreen','mediumslateblue','midnightblue'];
|
||||||
|
//document.getElementById('map');
|
||||||
|
for (var botId in newPointsByPlayer){
|
||||||
|
|
||||||
|
var tail = newPointsByPlayer[botId]['tail'];
|
||||||
|
for(var coordsIx in tail){
|
||||||
|
|
||||||
|
coords = tail[coordsIx];
|
||||||
|
//draw the point
|
||||||
|
var rect=createElemNS('rect',{'x':coords[0],'y':coords[1],'width':'2','height':'2','style':'fill:' + botsColor[botId] + ';'});
|
||||||
|
document.getElementById('map').appendChild(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function createElemNS(type,attributes){
|
||||||
|
//same as createElem but with ns for svg file
|
||||||
|
var elem=document.createElementNS("http://www.w3.org/2000/svg",type);
|
||||||
|
for (var i in attributes)
|
||||||
|
{elem.setAttributeNS(null,i,attributes[i]);}
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
function changeSelect(number,botId){
|
||||||
|
//show an other selector if bot is chosen
|
||||||
|
var next = parseInt(number) + 1;
|
||||||
|
if((botId > 0) && (number < 12)){
|
||||||
|
if(document.getElementById('selectBot' + next)){
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
show_bot_panel(next);
|
||||||
|
}
|
||||||
|
if(number > 0){
|
||||||
|
document.getElementById('fightButton').disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function show_bot_panel(number){
|
||||||
|
//configurePlayers
|
||||||
|
var fieldset = createElem('fieldset',{'class':'botFormulaire'});
|
||||||
|
var legend = createElem('legend',{});
|
||||||
|
legend.innerHTML = 'bot ' + number;
|
||||||
|
fieldset.appendChild(legend);
|
||||||
|
var p=createElem('p');
|
||||||
|
var select = createElem('select',{'id':'selectBot' + number, 'onchange':'changeSelect(' + number + ',this.value);'});
|
||||||
|
var option = createElem('option',{'value': '0', 'selected': 'selected','disabled':'disabled'});
|
||||||
|
option.innerHTML = '';
|
||||||
|
select.appendChild(option);
|
||||||
|
for (var i = 0; i < botsAvailable.length; i++){
|
||||||
|
var option = createElem('option',{'value': botsAvailable[i]['id']});
|
||||||
|
option.innerHTML = botsAvailable[i]['name'];
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
p.appendChild(select);
|
||||||
|
fieldset.appendChild(p);
|
||||||
|
|
||||||
|
document.getElementById('configurePlayers').appendChild(fieldset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyInitMessage(req,xd_check){
|
||||||
|
document.getElementById('fightButton').disabled=true;
|
||||||
|
//callback function when init game request
|
||||||
|
if(req.readyState == 4){
|
||||||
|
if(req.status == 200) {
|
||||||
|
//alert (req.responseText);
|
||||||
|
try{
|
||||||
|
var ret = JSON.parse(req.responseText);
|
||||||
|
}catch(e){
|
||||||
|
addLog('erreur' + req.responseText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addLog(ret['logs']);
|
||||||
|
if(ret['status'] == true){
|
||||||
|
growTails(ret['botsPosition']);
|
||||||
|
play(ret['gameId'],xd_check);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
alert ('error ' + req.status);
|
||||||
|
document.getElementById('fightButton').disabled=false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawMap(map){
|
||||||
|
console.log(map);
|
||||||
|
var botsColor = ['cyan','darkmagenta','darkred','darkslategrey','deeppink','dodgerblue','goldenrod','grey','indigo','lightgreen','mediumslateblue','midnightblue'];
|
||||||
|
|
||||||
|
for (var botId in map){
|
||||||
|
for(var coordsI in map[botId]){
|
||||||
|
coords = map[botId][coordsI];
|
||||||
|
//draw the point
|
||||||
|
var rect=createElemNS('rect',{'x':coords[0],'y':coords[1],'width':'2','height':'2','style':'fill:' + botsColor[botId] + ';'});
|
||||||
|
document.getElementById('map').appendChild(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function play(gameId,xd_check){
|
||||||
|
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.onreadystatechange = function(){
|
||||||
|
if(req.readyState == 4){
|
||||||
|
if(req.status == 200) {
|
||||||
|
addLog(req.responseText);
|
||||||
|
var reponse = JSON.parse(req.responseText);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
drawMap(reponse['lap']);
|
||||||
|
if(reponse['continue'] == '1'){
|
||||||
|
play(gameId,xd_check);
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
req.open("POST", '/tron', true);
|
||||||
|
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
req.send('act=play&xd_check=' + xd_check + '&gameId=' + gameId + '&fullLogs=' + document.getElementById("fullLogs").checked);
|
||||||
|
}
|
||||||
|
function tron(xd_check){
|
||||||
|
//empty
|
||||||
|
while (document.getElementById('fightResult').firstChild) {
|
||||||
|
document.getElementById('fightResult').removeChild(document.getElementById('fightResult').firstChild);
|
||||||
|
}
|
||||||
|
// draw border;
|
||||||
|
var svg = createElemNS('svg',{'id':'map','width':'500','height':'500','viewBox':'0 0 1000 1000'});
|
||||||
|
var rect=createElemNS('rect',{'x':'0','y':'0','width':'1000','height':'1000','style':'stroke:#000000; fill:none;'});
|
||||||
|
svg.appendChild(rect);
|
||||||
|
document.getElementById("fightResult").appendChild(svg);
|
||||||
|
|
||||||
|
var plogs = createElem("p",{'id':'logs'});
|
||||||
|
document.getElementById("fightResult").appendChild(plogs);
|
||||||
|
//get bot list
|
||||||
|
var botsList=[];
|
||||||
|
var i=0;
|
||||||
|
while(document.getElementById('selectBot' + i)){
|
||||||
|
botsList.push(document.getElementById('selectBot' + i).value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ask arena to send bots init messages
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.onreadystatechange = function(){applyInitMessage(request,xd_check)};
|
||||||
|
request.open("POST", '/tron', true);
|
||||||
|
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
request.send('act=initGame&xd_check=' + xd_check + '&bots=' + JSON.stringify(botsList) + '&fullLogs=' + document.getElementById("fullLogs").checked);
|
||||||
|
}
|
32
src/arenas/tron/public.php
Normal file
32
src/arenas/tron/public.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
#- BEGIN LICENSE BLOCK ---------------------------------------
|
||||||
|
#
|
||||||
|
# This file is part of botsArena.
|
||||||
|
#
|
||||||
|
# Copyright (C) Gnieark et contributeurs
|
||||||
|
# Licensed under the GPL version 3.0 license.
|
||||||
|
# See LICENSE file or
|
||||||
|
# http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
||||||
|
#
|
||||||
|
# -- END LICENSE BLOCK -----------------------------------------
|
||||||
|
|
||||||
|
require_once(__DIR__."/functions.php");
|
||||||
|
$bots=get_Bots_Array('tron');
|
||||||
|
$botsArr=array();
|
||||||
|
foreach($bots as $bot){
|
||||||
|
$botsArr[]=array('id' => $bot['id'], 'name' => $bot['name']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<article id="mainArticle">
|
||||||
|
<h2><?php echo $lang['MAKE_DUEL'];?></h2>
|
||||||
|
<aside id="configurePlayers">
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
<script>
|
||||||
|
var botsAvailable = <?php echo json_encode($botsArr); ?>;
|
||||||
|
show_bot_panel(0);
|
||||||
|
</script>
|
||||||
|
<p><input type="checkbox" id="fullLogs"/><label for="fullLogs">view the full logs</label></p>
|
||||||
|
<p><input id="fightButton" disabled="disabled" type="button" value="<?php echo $lang['FIGHT']; ?>" onclick="tron('<?php echo xd_check_input(2); ?>');"></p>
|
||||||
|
<div id="fightResult"></div>
|
||||||
|
</article>
|
4
src/arenas/tron/style.css
Normal file
4
src/arenas/tron/style.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#configurePlayers{width: 100%;}
|
||||||
|
#configurePlayers fieldset{display: block; width: 100px; float:left;}
|
||||||
|
#logs{display:block;padding-left:10px; height: 200px; overflow-y: scroll;}
|
||||||
|
#logs p em {color: grey; font-size: 70%; test-transform:italic;}
|
27
src/arenas/tron/test/CoordsTest.php
Normal file
27
src/arenas/tron/test/CoordsTest.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
require_once '../Coords.php';
|
||||||
|
require_once '../Direction.php';
|
||||||
|
|
||||||
|
class CoordsTest extends TestCase {
|
||||||
|
|
||||||
|
|
||||||
|
public function testCirculaire(){
|
||||||
|
$startCoord = new Coords(15,3);
|
||||||
|
$endCoord = $startCoord->addDirection(Direction::make('x+'))
|
||||||
|
->addDirection(Direction::make('y-'))
|
||||||
|
->addDirection(Direction::make('x-'))
|
||||||
|
->addDirection(Direction::make('y+'));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$this->assertTrue($endCoord == $startCoord);
|
||||||
|
}
|
||||||
|
public function testIsDifferent(){
|
||||||
|
$startCoord = new Coords(15,3);
|
||||||
|
$endCoord = $startCoord->addDirection(Direction::make('x+'));
|
||||||
|
fwrite(STDERR, $startCoord ."\n");
|
||||||
|
fwrite(STDERR, $endCoord ."\n");
|
||||||
|
$this->assertFalse($endCoord == $startCoord);
|
||||||
|
}
|
||||||
|
}
|
71
src/arenas/tron/test/DirectionTest.php
Normal file
71
src/arenas/tron/test/DirectionTest.php
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
require_once '../Direction.php';
|
||||||
|
|
||||||
|
class Directiontest extends TestCase {
|
||||||
|
public function invalidStrings() {
|
||||||
|
return array(
|
||||||
|
array('jhgjhg'),
|
||||||
|
array('X+'),
|
||||||
|
array(4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @dataProvider invalidStrings
|
||||||
|
* @expectedException invalidDirectionException
|
||||||
|
*/
|
||||||
|
public function testRejectInvalidString($invalidString){
|
||||||
|
Direction::make($invalidString);
|
||||||
|
}
|
||||||
|
public function validStrings(){
|
||||||
|
return array(
|
||||||
|
array('x+'),
|
||||||
|
array('y+'),
|
||||||
|
array('x-'),
|
||||||
|
array('y-'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider validStrings
|
||||||
|
*/
|
||||||
|
public function testAcceptValidString($validString){
|
||||||
|
$this->assertInstanceOf(Direction::class,Direction::make($validString));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider validStrings
|
||||||
|
*/
|
||||||
|
public function testDeltaXY($validString){
|
||||||
|
$dir = Direction::make($validString);
|
||||||
|
$this->assertTrue($dir->deltaX != 0 || $dir->deltaY != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider validStrings
|
||||||
|
*/
|
||||||
|
public function testToString($validString){
|
||||||
|
$this->assertTrue(Direction::make($validString) == $validString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider validStrings
|
||||||
|
*/
|
||||||
|
public function testOpposite($validString){
|
||||||
|
$dir = Direction::make($validString);
|
||||||
|
$op = $dir->opposite();
|
||||||
|
|
||||||
|
$this->assertInstanceOf(Direction::class,$op);
|
||||||
|
$this->assertFalse($dir == $op);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider validStrings
|
||||||
|
*/
|
||||||
|
public function testOppositeOpposite($validString){
|
||||||
|
$dir = Direction::make($validString);
|
||||||
|
$opop = $dir->opposite()->opposite();
|
||||||
|
$this->assertTrue($dir == $opop);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
src/arenas/tron/test/TrailTest.php
Normal file
64
src/arenas/tron/test/TrailTest.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
require_once '../Direction.php';
|
||||||
|
require_once '../Coords.php';
|
||||||
|
require_once '../TronPlayer.php';
|
||||||
|
|
||||||
|
class TronPlayerTest extends TestCase {
|
||||||
|
public function validPlayer(Direction $direction) {
|
||||||
|
return new TronPlayer(
|
||||||
|
'http://127.0.0.1',
|
||||||
|
'test',
|
||||||
|
new Coords(0, 0),
|
||||||
|
$direction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTronPlayerCreation() {
|
||||||
|
$this->assertInstanceOf(
|
||||||
|
TronPlayer::class,
|
||||||
|
$this->validPlayer(Direction::make('x+'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function directions() {
|
||||||
|
return array(
|
||||||
|
array(Direction::make('x+')),
|
||||||
|
array(Direction::make('x-')),
|
||||||
|
array(Direction::make('y+')),
|
||||||
|
array(Direction::make('y-')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider directions
|
||||||
|
* @expectedException OppositeForbiddenException
|
||||||
|
*/
|
||||||
|
public function testOppositeForbidden(Direction $direction) {
|
||||||
|
$player = $this->validPlayer($direction);
|
||||||
|
$player->changeDirection($direction->opposite());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAlreadyLost() {
|
||||||
|
$right = Direction::make('x+');
|
||||||
|
$down = Direction::make('y-');
|
||||||
|
$left = Direction::make('x-');
|
||||||
|
$up = Direction::make('y+');
|
||||||
|
|
||||||
|
$player = $this->validPlayer($right);
|
||||||
|
$player->nextMove($right);
|
||||||
|
$player->nextMove($down);
|
||||||
|
$player->nextMove($left);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$player->nextMove($up);
|
||||||
|
throw new Exception('TronPlayer did not throw AlreadyPlayedException');
|
||||||
|
} catch(AlreadyPlayedException $e) { }
|
||||||
|
|
||||||
|
try {
|
||||||
|
$player->nextMove($up);
|
||||||
|
throw new Exception('TronPlayer did not throw AlreadyLostException');
|
||||||
|
} catch(AlreadyLostException $e) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,14 @@ $arenas=array(
|
||||||
'jsFile'=> "js.js",
|
'jsFile'=> "js.js",
|
||||||
'cssFile'=> "style.css",
|
'cssFile'=> "style.css",
|
||||||
'ludusUrl' => "/testBotScripts/connectfour.html"
|
'ludusUrl' => "/testBotScripts/connectfour.html"
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'id' => "tron",
|
||||||
|
'url' => "/tron",
|
||||||
|
'title' => "Tron",
|
||||||
|
'metaDescription' => 'Affrontements de bots à Tron',
|
||||||
|
'jsFile'=> "js.js",
|
||||||
|
'cssFile'=> "style.css"
|
||||||
)
|
)
|
||||||
|
|
||||||
);
|
);
|
|
@ -151,7 +151,7 @@ function conn_bdd(){
|
||||||
}
|
}
|
||||||
mysqli_select_db($linkMysql,$mysqlParams['database']);
|
mysqli_select_db($linkMysql,$mysqlParams['database']);
|
||||||
mysqli_set_charset($linkMysql, 'utf8');
|
mysqli_set_charset($linkMysql, 'utf8');
|
||||||
return $linkMysql; //does PHP can do that?
|
return $linkMysql;
|
||||||
|
|
||||||
}
|
}
|
||||||
function get_battles_history($game){
|
function get_battles_history($game){
|
||||||
|
@ -229,18 +229,24 @@ function ELO_get_new_ranks($elo1,$elo2,$score){
|
||||||
$elo2 + ELO_get_k($elo2) * (1 - $score - (1/ (1 + pow(10,(($elo1 - $elo2) / 400)))))
|
$elo2 + ELO_get_k($elo2) * (1 - $score - (1/ (1 + pow(10,(($elo1 - $elo2) / 400)))))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function save_battle($game,$bot1,$bot2,$resultat){
|
|
||||||
|
function save_battle($game,$bot1,$bot2,$resultat,$nameOrIds = 'name'){
|
||||||
|
//$bots1 and $bots2 are bots'names
|
||||||
//resultat: 0 match nul, 1 bot1 gagne 2 bot 2 gagne
|
//resultat: 0 match nul, 1 bot1 gagne 2 bot 2 gagne
|
||||||
|
|
||||||
global $lnMysql;
|
global $lnMysql;
|
||||||
|
|
||||||
$game=substr($game,0,8); //limit 8 char for limitting mysql index size
|
$game=substr($game,0,8); //limit 8 char for limitting mysql index size
|
||||||
|
|
||||||
|
if($nameOrIds == "name"){
|
||||||
//chercher les id de bot 1 et bot2
|
//chercher les id de bot 1 et bot2
|
||||||
$rs=mysqli_query($lnMysql,"SELECT name,id,ELO FROM bots
|
$rs=mysqli_query($lnMysql,"SELECT name,id,ELO FROM bots
|
||||||
WHERE name='".mysqli_real_escape_string($lnMysql,$bot1)."'
|
WHERE name='".mysqli_real_escape_string($lnMysql,$bot1)."'
|
||||||
OR name='".mysqli_real_escape_string($lnMysql,$bot2)."'");
|
OR name='".mysqli_real_escape_string($lnMysql,$bot2)."'");
|
||||||
|
}else{
|
||||||
|
$rs = mysqli_query($lnMysql, "SELECT name,id,ELO FROM bots
|
||||||
|
WHERE id='".mysqli_real_escape_string($lnMysql,$bot1)."'
|
||||||
|
OR id='".mysqli_real_escape_string($lnMysql,$bot2)."'");
|
||||||
|
}
|
||||||
while($r=mysqli_fetch_row($rs)){
|
while($r=mysqli_fetch_row($rs)){
|
||||||
$bots[$r[0]]=$r[1];
|
$bots[$r[0]]=$r[1];
|
||||||
$actualELO[$r[0]]=$r[2];
|
$actualELO[$r[0]]=$r[2];
|
||||||
|
|
Loading…
Reference in New Issue
Block a user