lunes, 26 de mayo de 2008

IWebMvc Milestone 5 released

I expected to have a Release Candidate by now but I couldn't get it in time so I'm releasing an intermediate release, Milestone 5, that packs a couple new widgets along with preliminary documentation. It's available at the repository.

The first widget deals with addresses and integrates Google Maps with IWebMvc. A nice feature to have indeed.

The second adds sound (MP3) support based on Sound Manager 2, a nice JS library. It includes a really well shaped player.

The codebase is frozen and just bug fixes (mainly in IE7) till the 1.0 release which I expect will be soon.

PS.- IWebMvc loads the browser with requests and hits the two connection per server limit pretty fast. FF3 has upped the limit to eight concurrent connections (IE8 has done it as well but dojo is still unsupported there) so it's highly recommended!

domingo, 11 de mayo de 2008

Creating a playing bot in Java

So, I've just read an interesting article at DZone about how to build a poker bot today. Nonetheless it was somehow misleading. After reading it, one can get the impression that to build a bot C++ knowledge is a prerequisite. And advanced Windows programming as well. In all fairness, they are not.

In fact, any developer with basic Java understanding can code a bot in less than a day. Of course, everything depends on the kind of game...a table game (read Poker or BlackJack) is achievable. WoW is not. I'll show you how building a roulette bot in Java step by step.

First of all, I won't provide the FULL source code. Gambling (and even more so online gambling) bears some subtleties that make it hazardous. That's also the reason I've chosen roulette: no bot or betting system can ever beat the house edge in the log run (read the wizard of odds if in doubt). This is good for us though, the bot is useless (in the long run) so the reader should be just interested in the techniques and not the product.

Roulette is a simple game. Past events cannot influence the outcome of future spins so a player just needs to place his bets and wait until the ball lands in a number. Of course some AI would be useful though. But in the end the bot just needs to be able to bet and recognize the table state.

So basically there's an input and some actions. The input needs to be captured from the screen and the actions are performed controlling the mouse. This tasks can be done using a Java class, java.awt.Robot.

The OCR
Trying to get true OCR is not really feasable. Fortunately, there's a shortcut. First, the bot just needs to scan portions of the screen, second the data is fixed and third the screen is updated from time to time. For example, when the bot wants to scan the outcome of a spin just needs to check for numbers 0 to 36 in some fixes coordinates.

The first task of the bot developer is to save image files with all the possible outcomes. In roulette this means 40 images (37 numbers and three extra button images to check if the bot can bet at a given time). I use AutoIt Window Info to get screen coordinates quickly. Then it's a question of using the createScreenCapture method of the Robot class and the Java ImageIO API to save a PNG of each location.

Rectangle spinResultArea = new Rectangle(100, 100, 50, 15);
BufferedImage res = robot.createScreenCapture(spinResultArea);
ImageIO.write(res, "png", new File(...));

Once generated (the code is disposable now) the images must be packaged inside the JAR and loaded using:

InputStream img = getClass().getResourceAsStream("/resources/0.png");
BufferedImage number = ImageIO.read(img);

As a matter of fact, it's better to have an array of pixels to make the comparisons so:

protected int[] getData(BufferedImage im) {
   int[] rgb = new int [im.getWidth() * im.getHeight()];
   image.getRGB(0, 0, im.getWidth(), im.getHeight(), rgb, 0, im.getHeight());
   return rgb;
}

Load all the images on startup and save the generated arrays in a List for later. The last part of the OCR routine is comparing images. This is easy:

int[] rgb = getData(sourceImg);
Arrays.equals(rgb, numbers.get(number))

The mouse
The Robot class controls the mouse (or keyboard) fine. A simple function will suffice:

protected void click(int[] screenPosition) {
   robot.mouseMove(screenPosition[0], screenPosition[1]);
   robot.delay(75);
   robot.mousePress(InputEvent.BUTTON1_MASK);
   robot.delay(150);
   robot.mouseRelease(InputEvent.BUTTON1_MASK);
   robot.delay(150);
}

The code is self explanatory really. Read the javadoc if in doubt.

The AI
The bot should be fully functional by now. Of course, it's not very intelligent. All bots need some kind of AI. And unfortunately, the AI is probably the difficult part. For roulette the AI is limited to selecting a betting system and coding it. The cancellation system should work. Just one advice here, use unit tests! The number of combinations for a table game is huge so manually testing them becomes a daunting task really quick. Creating unit tests, in addition to saving time, ensures the code is bug free (to some extent) which comes handy when playing for real money, I guess.

And that's really all it takes. No DLL hooks or C++ ;-)