sparks wrote:
How will participants be coding the ai? Are we limited to custom functions created for the game for interaction? So like:
if(spaceToLeftFree){
moveLeft(1);
}
sort of thing?
You code a single anonymous function that takes the state of the board and returns the x,y co-ordinates of where you want to move. (I think. )
Offline
I would prefer the board to be number-based rather than string-based (e.g. red = 1, blue = -1, empty = 0).
I'll admit, I'm still looking at the code, but, just to be clear, should the "evaluation function" (which is essentially what the competitors are writing) be able to evaluate the board for either side?
function Evaluate_Best_Move(side_to_move) { }
Offline
@MoreGamesNow
In some sense, you have to. If you're trying to make a strategic move, you also have to consider what the best move for your opponent is; what you have to prevent. I guess an ideal algorithm would check all the possible moves to some (large) depth in the future for both players and then select the one that will most likely lead to the most favorable outcome.
Also, if you don't want to use strings, you could just use an enum.
@Hardmath123
It just occurred to me: do you think there should be a limit on the processing time each function is allowed? You could have a Boolean flag somewhere inside each competing function that forces a return if it exceeds the time limit, and results in some sort of "null" value for the "desiredMove" object (I'm guessing you'll make a class that represents a move). That value would indicates a forfeit or some sort of penalty, I don't know what the rules might be.
Last edited by amcerbu (2012-05-16 00:03:08)
Offline
amcerbu wrote:
@MoreGamesNow
In some sense, you have to. If you're trying to make a strategic move, you also have to consider what the best move for your opponent is; what you have to prevent. I guess an ideal algorithm would check all the possible moves to some (large) depth in the future for both players and then select the one that will most likely lead to the most favorable outcome.
Also, if you don't want to use strings, you could just use an enum.
@Hardmath123
It just occurred to me: do you think there should be a limit on the processing time each function is allowed? You could have a Boolean flag somewhere inside each competing function that forces a return if it exceeds the time limit, and results in some sort of "null" value for the "desiredMove" object (I'm guessing you'll make a class that represents a move). That value would indicates a forfeit or some sort of penalty, I don't know what the rules might be.
No enums in JavaScript!
Offline
roijac wrote:
Hardmath123 wrote:
@Roijac: Here's the basic code which we need to fill in, test, debug, etc. Anyone is welcome to complete a method for me. You'll get to see how the backend works so your AI will work out better, too!
Code:
codethanks!
i'm working on the whoWon method right now, though i'm using a bit another approach (caching if a piece is connected to one side, then marking the pieces around it as also connected, etc.)
Yeah, that's practically what I meant. Then you don't need the connected method. By the way, this is a cool little CS exercise in itself, so pat yourself on the back once you get it.
little comment: there cannot be tie
Oops, sorry about that. As I said, I wrote that really late at night.
Is red or blue playing from left to right?
You decide. Remember, the AIs should always be given a board rotated so they play top-to-bottom. That's why you need a rotate method.
Last edited by Hardmath123 (2012-05-16 04:33:35)
Offline
amcerbu wrote:
@MoreGamesNow
In some sense, you have to. If you're trying to make a strategic move, you also have to consider what the best move for your opponent is; what you have to prevent. I guess an ideal algorithm would check all the possible moves to some (large) depth in the future for both players and then select the one that will most likely lead to the most favorable outcome.
Also, if you don't want to use strings, you could just use an enum.
True. The difference is which branches are min/max, but I guess you can build that in using a "whoseTurn" variable.
Offline
Ok, to clear things up:
You write 1 anonymous function which takes in a board and returns the board with the move done on it. Now, given a board you should be able to calculate whose turn it is, right?
There is a time limit; namely the Safari computation timeout popup. If your script triggers the popup, the other player automatically wins. Note that my computer is pretty fast, so it shouldn't be too much of a problem unless you fall into an infinite loop or some enormous calculation.
There is a "class" (JS object) which represents a board. The back-end basically bounces the board back and forth between the two AIs, checking for cheating and wins in between. The JS object, HexBoard, also provides basic utilities like telling who won, possible moves, rotating the board, etc. You will be given a copy of the "official" board to work with.
Does that clear things up?
Offline
Hardmath123 wrote:
You write 1 anonymous function which takes in a board and returns the board with the move done on it.
Why not simplify it further and have it just return the [x ,y] co-ordinates of where to move? That should make the code to check for cheating simpler, too.
Now, given a board you should be able to calculate whose turn it is, right?
Why would you need to do that? I thought the function would be called to take your move, so you already know it's your turn...
Offline
Yeah, but ideally your function should have to be recursive, so you'll have to calculate that anyway. Besides, it's cleaner to just provide a board in and a board out. Finally, HexBoard will have a "whoseMove" method.
Returning a completed board, again, just sounds neater. It would make your AI easier to write, too. Normally, you should be speaking in terms of boards, rather than moves, all the time. I don't want to have to support a "move" protocol; just boards are enough.
Offline
Hardmath123 wrote:
Yeah, but ideally your function should have to be recursive, so you'll have to calculate that anyway. (...) HexBoard will have a "whoseMove" method.
Why? Surely you'd want to pass this to your recursive algorithm as a parameter — having to count pieces would make it slower
Besides, it's cleaner to just provide a board in and a board out. (...) Returning a completed board, again, just sounds neater. It would make your AI easier to write, too. Normally, you should be speaking in terms of boards, rather than moves, all the time. I don't want to have to support a "move" protocol; just boards are enough.
I'm not sure, you know... It still seems more complicated. The checking code would be more complicated, as you need to check all the squares to make sure only one piece of the right color has been added — rather than just checking the square [x,y] is empty. So surely you have to write more code, which doesn't seem neater.
MoreGamesNow wrote:
I would prefer the board to be number-based rather than string-based (e.g. red = 1, blue = -1, empty = 0).
This'd be quite nice, actually; then you can just flip the signs when rotating the board for the other player.
Offline
Hey, that's a cool idea (I mean the -1/+1 one)! Roijac, can you please add that in? You can just find-and-replace "R" and "B" with -1 and 1, and "-" with 0.
I suppose you have a point there, Blob, about efficiency. I really had my heart set on board-in-board-out mechanisms, but now it does look better to just ask for the coordinates... Ok, so you have to return the coordinates rather than the board. *sigh*
I'm still not convinced about passing whoseMove as an argument: in an ideal HexBoard function, the moveTo function will automatically switch whoseMove once a player has moved. There is no calculation needed at all, you simply switch from -1 to 1, which is literally multiplying by -1. I see no need for counting there. I guess that was my fault in not explaining properly, though, whoseMove would be a property, not a method. Sorry.
Offline
Hardmath123 wrote:
I suppose you have a point there, Blob, about efficiency. I really had my heart set on board-in-board-out mechanisms, but now it does look better to just ask for the coordinates... Ok, so you have to return the coordinates rather than the board. *sigh*
Sorrrrry...
I'm still not convinced about passing whoseMove as an argument: in an ideal HexBoard function, the moveTo function will automatically switch whoseMove once a player has moved. There is no calculation needed at all, you simply switch from -1 to 1, which is literally multiplying by -1. I see no need for counting there. I guess that was my fault in not explaining properly, though, whoseMove would be a property, not a method. Sorry.
I'm very confused now
I simply meant to point out that any internal AI code (eg. a closure/lambda function used to search possible moves, like a game tree) could keep track of whose move it's deciding without having to count squares.
Let me try and clarify what I'm thinking...
The "rotate" function would flip the x/y co-ordinates — in terms of transformations, a reflection in y=x. (I think you're referring to rows/cols, but same thing.) Rotating by 90º would only get back to the original rotation every 4 moves; which doesn't seem right, somehow. It would also inverse the signs, by multiplying by -1, as you're saying. In this way, the function "flips" all the details for the other player, and will flip back to be exactly the way they were before when you rotate the board again.
I don't think you really need a "whoseMove" method, or property, at all —except perhaps internally to the code that "plays"/competes the two AIs (and rotates between goes, checks and then executes their move, and so on).
The AI function is written assuming that it's "its" go, so you don't need to tell it whose move it is (as we've rotated the board and flipped the signs for it).
That's what I was thinking — does that help?
Offline
Okay, just to make things clear, is this the Hex game you were talking about?
You have to place a stone on a rhomboidal (11 x 11 hexagons, I think) board to "connect" two opposite sides of the board? So basically it's really fancy Tic-Tac-Toe.
Offline
amcerbu wrote:
Okay, just to make things clear, is this the Hex game you were talking about?
You have to place a stone on a rhomboidal (11 x 11 hexagons, I think) board to "connect" two opposite sides of the board? So basically it's really fancy Tic-Tac-Toe.
http://upload.wikimedia.org/wikipedia/c … 282%29.jpg
Yep.
I'll reply to blob's post once I understand it...
Offline
Yay, I'm finally satisfied with my HTML/CSS layout:
/* Hexathon styling * HexBoxes.css * (c) 2012 Hardmath123 */ body { height:100%; width:100%; margin:0px; padding:0px; overflow:hidden; background-color:lightGray; } div.topbar { height:90px; width:auto; padding:5px; overflow:hidden; text-shadow:3px 3px 3px #333; } div.leftbar { float:left; height:inherit; width:400px; padding:2px; border-right-color:gray; border-right:width:1px; border-right-style:dashed; } div.boardbox { float:right; height:inherit; width:750px; } div.footer { position:absolute; bottom:0px; width:95%; margin-bottom:0px; text-align:right; height:30px; } div.contentarea { position:relative; height:500px; width:1000px; } div.barcontent { font-family:monospace; width:400px; } div.tabsbox { height:34px; background-color:gray; overflow:hidden; border-radius:5px; -webkit-border-radius:5px; -moz-border-radius:5px; } div.tab { width:70px; height:30px; float:left; font-family:monospace; background-color:gray; color:white; padding:2px; } div.tab.special { float:right !important; } div.tab:hover { background-color:black; color:gray; } div.tab:active { color:black; } div.tabcontent { width:inherit; } h1 { margin:0px; padding:0px; font-family:monospace; font-size:80px; color:gray; } textarea.codebox { resize:vertical; width:85%; height:200px; font-family:monospace; background-color:lightGray; border-style:dashed; border-color:gray; border-width:2px; max-height:400px; margin-top:4px; margin-right:4px; margin-bottom:4px; margin-left:30px; padding:5px; } textarea.codebox:focus { border-style:dashed; border-color:gray; border-width:2px; outline:none; }
<!DOCTYPE html> <html> <head> <title>Hexathon</title> <style type="text/css"> @import url(HexBoxes.css); </style> <script type="text/javascript" src="HexUI.js"></script> <script type="text/javascript" src="HexFramework.js"></script> </head> <body> <div class="topbar" id="titlebar"> <h1>Hexathon 1.1</h1> </div> <div class="contentarea" id="contentarea"> <div class="leftbar" id="leftbar"> <div class="tabsbox"> <div class="tab"> Your Code </div> <div class="tab"> Against Code </div> <div class="tab"> Rules </div> <div class="tab special" id="startgame"> Start game </div> </div> <div class="barcontent"> <div class="tabcontent" id="tabA"> <strong>Complete the function below:</strong> <br/> function move(<strong>board</strong>) {<br/> <center><textarea class="codebox" id="testCode"></textarea></center><br/> } </div> <div class="tabcontent" id="tabB"> <input type="radio" name="opponentPick" checked value="rand" class="opponentMove"/> Random Move<br/> <input type="radio" name="opponentPick" value="user" class="opponentMove"/> You move<br/> </div> <div class="tabcontent" id="tabC"> • • • • </div> </div> </div> <div class="boardbox" id="boardbox"> <canvas id="world" height=500 width=500></canvas> </div> </div> <div class="clearer-for-footer" id="footerclearer"></div> <div class="footer" id="footer">© Hardmath123 2012</div> </body> </html>
// HexUI.js window.onload=function() { window.onresize(); // Resize Window //Mouse stuff window.mouse = {x:0, y:0, down:0}; document.onmousemove=function(event) { mouse.x=event.clientX; mouse.y=event.clientY; } document.onmouseup=function() { mouse.down=false; } document.onmousedown=function() { mouse.down=true; } // Frames document.getElementById("testCode").value="/* \n\tType your code here\n*/"; document.getElementById("tabA").style.display="block"; document.getElementById("tabB").style.display="none"; document.getElementById("tabC").style.display="none"; document.getElementsByClassName("tab")[0].onclick=function() { document.getElementById("tabA").style.display="block"; document.getElementById("tabB").style.display="none"; document.getElementById("tabC").style.display="none"; }; document.getElementsByClassName("tab")[1].onclick=function() { document.getElementById("tabB").style.display="block"; document.getElementById("tabA").style.display="none"; document.getElementById("tabC").style.display="none"; }; document.getElementsByClassName("tab")[2].onclick=function() { document.getElementById("tabC").style.display="block"; document.getElementById("tabA").style.display="none"; document.getElementById("tabB").style.display="none"; }; // This is the actual game engine document.getElementById("startgame").onclick=function() { var globalBoard = new HexBoard(); globalBoard.moveTo(5,5,"R"); globalBoard.drawOn(document.getElementById("world").getContext("2d")); } // How does the opponent move? // Currently it just alerts which radio was clicked, but // it should set a global variable which decides the opponent // script. var opponentMoves = document.getElementsByClassName("opponentMove"); for (var i=0; i<opponentMoves.length; i++) { opponentMoves[i].onclick=function() { alert(this.value); } } } window.onbeforeunload = function (evt) {// Ask before unloads var e = evt || window.event, _msg = "..."; if (e) { e.returnValue = _msg; } return _msg; }; window.onresize=function(){ var windowDimensions = [document.documentElement.clientWidth, document.documentElement.clientHeight]; if (windowDimensions[0]>=1160) { document.getElementById("contentarea").style.width=windowDimensions[0]+"px" } else { document.getElementById("contentarea").style.width="1160px" } }
Enjoy playing around with it!
Offline
blob8108 wrote:
The AI function is written assuming that it's "its" go, so you don't need to tell it whose move it is (as we've rotated the board and flipped the signs for it).
Ah, you're confused there, and so was I until I spent a 1.5 hour bus ride thinking about how exactly this should go. The problem is, the AI doesn't know who "it" is in the first place! You see, it knows it's "its" turn, because that's why it is being run; but it doesn't know who its move should support. There are two ways to convey this information: either a) it's passed as an argument, or b) the board contains a property stating who should move next. I think "b" would be cleaner, because your code would be more "neutral".
As for rotation, here's a thought: let's just assume -1 goes left-to-right and +1 goes top-to-bottom; and we'll keep that as a game convention/rule. The AI itself already knows who's move it is (by the property), so it then automatically which direction it should make a chain in. By this logic, we don't actually need to rotate the board: we just need to tell the AI function who's move it is and the AI would think in terms of that direction.
So, to recap, at the moment I think we should replace the "rotate" function with a brand-new "whoseMove" property which the back-end negates every time it bounces the board back and forth. The AIs need to support both players. Depending on "whoseMove" it is, they aim to make a chain in that direction. Of course, our framework should support the whoWon method such that it conforms to the above convention: -1 plays horizontally, +1 plays vertically. We do not edit the board at all (no rotation, negating of pieces, etc.), we just place the piece and move it along to the next player, checking for cheating along the way. This would be much simpler to implement by both us and the AI coders.
So, to recap the recap ( ) : no rotation, just a new property with the current player's name. AIs support both players. How does that sound?
Offline
Hardmath123 wrote:
blob8108 wrote:
The AI function is written assuming that it's "its" go, so you don't need to tell it whose move it is (as we've rotated the board and flipped the signs for it).
Ah, you're confused there, and so was I until I spent a 1.5 hour bus ride thinking about how exactly this should go. The problem is, the AI doesn't know who "it" is in the first place! You see, it knows it's "its" turn, because that's why it is being run; but it doesn't know who its move should support.
I think I meant to say (I was indeed confused ) that all the AI functions are written as a particular player (say, +1/top-to-bottom), and return a move that best supports that side.
As for rotation, here's a thought: let's just assume -1 goes left-to-right and +1 goes top-to-bottom; and we'll keep that as a game convention/rule. The AI itself already knows who's move it is (by the property), so it then automatically which direction it should make a chain in. By this logic, we don't actually need to rotate the board: we just need to tell the AI function who's move it is and the AI would think in terms of that direction.
So, to recap, at the moment I think we should replace the "rotate" function with a brand-new "whoseMove" property which the back-end negates every time it bounces the board back and forth. The AIs need to support both players. Depending on "whoseMove" it is, they aim to make a chain in that direction. Of course, our framework should support the whoWon method such that it conforms to the above convention: -1 plays horizontally, +1 plays vertically. We do not edit the board at all (no rotation, negating of pieces, etc.), we just place the piece and move it along to the next player, checking for cheating along the way. This would be much simpler to implement by both us and the AI coders.
So, to recap the recap ( ) : no rotation, just a new property with the current player's name. AIs support both players. How does that sound?
May I disagree, again? Rotating the board seems simpler to me.
Writing AI code to support both directions is more complicated; you have to write code to handle both cases. In fact, wouldn't the easiest thing then be for the custom AI code to rotate the board itself if it's the -1 player, run its decision logic as if it's the +1/top-to-bottom player, and then rotate it back again? And this seems more easily implemented in your framework. We want the AIs to be easy to code, of course; so they can better concentrate on the actual AI component of making the decision.
So I stand by my opinion — the AIs should be coded to play only top-to-bottom, and not both players.
Here's some pseudocode for the framework, if that helps clarify any:
bob = bob's AI function fred = fred's AI function board = HexBoard thingy while 1: (x, y) = bob(board.copy()); if not board.isEmpty((x, y)): # Bob cheated! exit board.do_move((x, y), +1) if board.hasWon(): # Bob wins! board.rotate() (x, y) = fred(board.copy()); if not board.isEmpty((x, y)): # Fred cheated! exit board.do_move((x, y), +1) board.rotate() if board.hasWon(): # Bob wins!
Is this not simpler? [The hasWon() thing is irrelevant. ]
Edit: corrected the function.
Last edited by blob8108 (2012-05-17 06:41:13)
Offline
Hardmath123 wrote:
Enjoy playing around with it!
I like this! I can't wait for the UI/board to exist...
A couple of suggestions (sorry to be so annoying!) — maybe have a single HTML file with the styles/scripts embedded in the <head>? Thusly. And maybe it could save your code in localStorage, or something similar? The "leave page" request is a nice touch, too.
Offline
blob8108 wrote:
Hardmath123 wrote:
blob8108 wrote:
The AI function is written assuming that it's "its" go, so you don't need to tell it whose move it is (as we've rotated the board and flipped the signs for it).
Ah, you're confused there, and so was I until I spent a 1.5 hour bus ride thinking about how exactly this should go. The problem is, the AI doesn't know who "it" is in the first place! You see, it knows it's "its" turn, because that's why it is being run; but it doesn't know who its move should support.
I think I meant to say (I was indeed confused ) that all the AI functions are written as a particular player (say, +1/top-to-bottom), and return a move that best supports that side.
As for rotation, here's a thought: let's just assume -1 goes left-to-right and +1 goes top-to-bottom; and we'll keep that as a game convention/rule. The AI itself already knows who's move it is (by the property), so it then automatically which direction it should make a chain in. By this logic, we don't actually need to rotate the board: we just need to tell the AI function who's move it is and the AI would think in terms of that direction.
So, to recap, at the moment I think we should replace the "rotate" function with a brand-new "whoseMove" property which the back-end negates every time it bounces the board back and forth. The AIs need to support both players. Depending on "whoseMove" it is, they aim to make a chain in that direction. Of course, our framework should support the whoWon method such that it conforms to the above convention: -1 plays horizontally, +1 plays vertically. We do not edit the board at all (no rotation, negating of pieces, etc.), we just place the piece and move it along to the next player, checking for cheating along the way. This would be much simpler to implement by both us and the AI coders.
So, to recap the recap ( ) : no rotation, just a new property with the current player's name. AIs support both players. How does that sound?May I disagree, again? Rotating the board seems simpler to me.
Writing AI code to support both directions is more complicated; you have to write code to handle both cases. In fact, wouldn't the easiest thing then be for the custom AI code to rotate the board itself if it's the -1 player, run its decision logic as if it's the +1/top-to-bottom player, and then rotate it back again? And this seems more easily implemented in your framework. We want the AIs to be easy to code, of course; so they can better concentrate on the actual AI component of making the decision.
So I stand by my opinion — the AIs should be coded to play only top-to-bottom, and not both players.
Here's some pseudocode for the framework, if that helps clarify any:Code:
bob = bob's AI function fred = fred's AI function board = HexBoard thingy while 1: (x, y) = bob(board.copy()); if not board.isEmpty((x, y)): # Bob cheated! exit board.do_move((x, y), 1) if board.hasWon(): # Bob wins! board.rotate() (x, y) = fred(board.copy()); if not board.isEmpty((x, y)): # Fred cheated! exit board.do_move((x, y), 1) board.rotate() if board.hasWon(): # Bob wins!Is this not simpler? [The hasWon() thing is irrelevant. ]
Well, again, you have managed to convince me of something, though I'm not sure exactly what yet... maybe it'll come to me as I write out this reply.
What you're suggesting is that each player is a "Bob", so to speak. The AI doesn't need to know who it's playing for, it just needs to play for top-to-bottom. The problem I see there is that how does it know which pieces it put down? Unless you always put down a 1 and all pieces are negated each time behind the scenes, but that sounds rather ugly in my opinion.
Offline
BTW I added you as a friend; I just realized I hadn't already...
Offline
Well, again, you have managed to convince me of something, though I'm not sure exactly what yet... maybe it'll come to me as I write out this reply.
...
What you're suggesting is that each player is a "Bob", so to speak. The AI doesn't need to know who it's playing for, it just needs to play for top-to-bottom. The problem I see there is that how does it know which pieces it put down? Unless you always put down a 1 and all pieces are negated each time behind the scenes, but that sounds rather ugly in my opinion.
Correct — it knows that all its pieces are +1 pieces; as you said the top-to-bottom player is +1. And the placing of the pieces would be handled by the framework, as the function just returns the co-ordinates, I thought.
Offline
blob8108 wrote:
Hardmath123 wrote:
Enjoy playing around with it!
I like this! I can't wait for the UI/board to exist...
A couple of suggestions (sorry to be so annoying!) — maybe have a single HTML file with the styles/scripts embedded in the <head>? Thusly. And maybe it could save your code in localStorage, or something similar? The "leave page" request is a nice touch, too.
Yeah, you code will be saved as a cookie for you to go back to. I'm not done with that bit, but I needed to prove I was in fact doing something.
Technically, you shouldn't be putting in JS/CSS directly in a page too much. I'm not an expert here (ask nXIII who knows every runtime optimization flaw on the planet), but it does make for a rather long piece of code, and that hurts my eyes. It seems cleaner not to have to scroll down 150 lines of CSS to get to my JS.
Offline
Hardmath123 wrote:
Technically, you shouldn't be putting in JS/CSS directly in a page too much. I'm not an expert here (ask nXIII who knows every runtime optimization flaw on the planet), but it does make for a rather long piece of code, and that hurts my eyes. It seems cleaner not to have to scroll down 150 lines of CSS to get to my JS.
This is true; for a site with multiple pages, it's certainly better to have separate JS/CSS files (although, out of curiousity, why were you using @import in a <style> rather than <link>?). If you've just got a single file, it probably doesn't matter, I think — and a single file is mildly easier to distribute than multiple files are.
But I'm with you about the scrolling I was having a similar problem on a project I did recently...
Offline