Virtual Reality is a hot topic these days. A few weeks ago I had the opportunity to test an Oculus Rift with Touch Controllers. PlayStation VR and HTC Vive have also been released lately. Android Developers like me have their Cardboards, which are a very low-cost option.
With the release of their Pixel devices, Google announced the Daydream VR. Similar to the Cardboard, you place your mobile phone in the VR headset and don’t need additional high-end hardware. For 70€ it is still a low-cost solution, if you don’t factor in the expensive phones.
My first attempt at trying Daydream VR unfortunately was not successful. I got the small Pixel phone, which worked flawlessly except when being used in the Daydream VR headset. It had regular reboots, a problem many others around the web have as well. And even worse, it had extreme visual drift as you can see in the video below.
It is hard to tell how bad that visual drift is. Your vision turning around while your body tells you there is no change in orientation makes you feel sick within a minute.
So after playing around an making a factory reset, I decided to return the device and get the Pixel XL instead. Turned out this was a good choice. With the Pixel XL everything works flawlessly. Head tracking has no noticeable delay and the touch controller works great.
Compared to a Cardboard this setup is a great improvement. A Cardboard only has a single button for user interaction. The touch controller gives navigating a whole new dimension. In games it is used as a magic stick, for controlling a steering wheel or tilting a playground to move a ball around. Every game seems to have its own way of navigating around. I believe we will see a lot more navigation styles before a few will crystallize as standard.
While Daydream with the controller is much better than before, you also see what is still missing. Turning your head around works great, but moving is not possible at all. In a VR world like Fantastic Beasts I want to move around and look at the beasts from all sides. In most of the applications this is not possible.
Graphics are pretty good with the right game/application. The detail level is impressively close to an Oculus Rift. However, in both VR systems you recognize single pixels. Even a resolution of 2560×1440 pixels is not much in VR mode, because it has to split for two eyes and fill the whole viewport. But every current VR system has this problem.
Developing a game AI can be as much fun as playing, especially when creating your own computer opponent. I am going to present you a simple pattern that works for nearly all turn based game ai’s, especially where there is a defined set of possible moves. This pattern is powering my own Laska for over 6 years already and I have been using it before for personal 4-Wins and Tic-Tac-Toe games.
For this you don’t need a perfect solution, but something that can win against most human players. It should prevent making obvious mistakes and the strength must be easily adjustable. I will show an algorithm that works for two players, but can easily be extended to more.
In a turn based match we always have moves made by every player. When saying move, what I actually mean is a Ply, or half-move. That is, the move of only one player. Our game AI will look a defined number of half-moves into the future and find the best possible move.
Your game should have a state which can be classified as good or bad. So in chess, we have a winning situation when the King will be taken out in the next move. This is great for one player, and really bad for the other one. There is also a lot in between. Let’s say one player has more Pawns than the other. This would be a good indicator that he is in a stronger position. In Laska, a winning situation is when the other player has no more possible moves available.
The blue player has won. Note that this simple situation is faked and can never happen in the real game.
There is a simple pattern that works for all these round based games. It is based on a decision tree of all possible moves and the classification value of the game situation.
From the starting position in Laska, there are four movers with six possible moves. Every move will lead to a forced jump. After that, there are either two possible jumps or three possible moves, depending on which Pawn was moved at the beginning.
starting position with four possible moves
If you can attach a value of the game situation to each node in the graph, it will be easy to select the best move. You need to figure out the subtree with the best worst-case situation.
For this, all your game has to provide is two methods:
getPossibleMovesOfActivePlayer(field), which will return all the possible moves for the active player on a given field.
getValue(field), which will return a useful value of the situation. It should be high positive when player 1 has won and high negative when player 2 has won.
With these methods available, you can find out what is the best move:
The method starts with an initial value of Integer.MIN_VALUE. Then it considers all possible moves. If there is only one move available, this is automatically the best option. This is an optimization which gives tremendous speedups when using a high recursionDepth. The recursionDepth indicates the size of our graph. The more we look into the future, the stronger our ai gets.
In all other cases, where we have more than one possible moves, we calculate the value of all of them with the specified recursionDepth. The part with Random().nextInt(2) is included to make the ai less predictable. If you leave out this randomness, the computer will always play the exact same game, if you do so as well.
So what does getValueOfMove() do?
Calculate value of a move, with a given recursionDepth
First it creates a clone of the field object for further calculation. On the cloned field (nextField), the given move is applied and the active player changed to the next one. With this new field, we now calculate the value of the following field. The simple case is when recursionDepth is exactly 1. Then we add the field value for the active player and subtract the field value for the other player. In my case, the getValue() method only factors in the Pawns of one player. Therefore it has to be called twice.
The more complex case is when recursionDepth is greater than 1. Then we recursively call the method bestMove() from above with a recursionDepth reduced by one. If there is no possible move and the bestMove is null, then the active player has won the match. So we return 1000, a very high number that can only be reached in a winning situation.
If there is a bestMove, this is what the opponent will choose to do. So for our move, this bestMove is the worst case that can happen to us. Because the value of the bestMove us calculated from the other players point of view, we have to negate it with value = -nextAI.getValueOfMove(bestMove, recursionDepth – 1);
Calculating the value
As mentioned above, you need to provide the getValue() method specific to your game. It receives the game state as input and returns an integer describing how “good” it is for the current player. In the case of Laska it returns values in the range of -1000 to 1000. Most likely there will be no perfect or correct return value. You have to create your own method and fine tune it over time. The better your getValue() method gets, the stronger your AI will be and you don’t need to use a high recursionDepth.
The weakest getValue() method will only return a negative number for a lost match and a positive number for a won match, otherwise zero. In this case you would have to use a recursionDepth that calculates all possible moves until the end of the game. In some cases this might be an option. The perfect getValue() method will already know all possible outcomes and give you perfect values. Your algorithm therefore only has to use a recursionDepth of 1.
Since most of the time it is neither possible to calculate all possible moves until the end nor to create a perfect getValue() method, we have something in between. In Laska the values are calculated like this:
For all of my pawns add 10
Add 5 for every of my pawn slices below the top, until there is an opponents pawn slice
If pawn is an officer add another 20
Add 2 if a pawn is on an outer field, instead of in the middle
Red has a value of 37, because there is a red pawn (+10), it is an officer (+20), there is another red pawn slice below (+5) and it stands on an outer field (+2)
For other games, think of indicators that obviously help towards winning. If pawns are taken out of the game compare the number of pawns. If you need 4 in a row to win give some value to 3 in a row, at least if there is still space for a 4th.
AI is a poster child for using Unit Tests. In fact, you will have a hard time if you leave them out. Especially annoying are minor mistakes, that don’t break your algorithm but weaken the ai significantly. I have started without proper testing and ran into that problem far too often. Some years ago I realized the value of testing and can iterate much faster now.
You can easily test all the methods with synthetic input. Does field1 give a higher value than field2? Make sure the best move in field1 for player 1 is X. Whenever you think My AI should make this move now but it is not, create a test case and fix it. Either you will find the mistake, or realize that the AI in fact was correct and you were wrong. This way you will soon have a stable and strong AI.
Another cool and fun usage is to let one version of the game AI play against an improved one. During optimizing the getValue() method, I could only believe a change would make the computer stronger. This was until I created a test that played 100 matches of the new version against the old one. Now there is an evaluation of the strength and I can be sure whether my change is an improvement or just a change.
The algorithm above is not meant for winning a competition or to be perfectly efficient. If you want this, you can start with Peter Norvigs Artificial Intelligence: A Modern approach and read through some current papers. However, the algorithm is a pattern that works for nearly all turn based games and gives decent results, so you can move on creating all the other important aspects.
Some things that can be improved:
In every getValueOfMove() the whole field object is cloned. While this wastes some computation time, I found that the algorithm is still fast enough even on mobile phones. The field objects are not too big and cloning them simplifies the following steps. Also this makes it more easy to parallelize computation if that would be necessary.
After bestMove has been calculated, the value is calculated by stepping into the same recursion again: value = -nextAI.getValueOfMove(bestMove, recursionDepth – 1);
You can tune the randomness in bestMove() by collecting all moves of the same value and make an equally weighted choice between them.
If you can think of more, please tell me in the comments.
As you can see, implementing a computer opponent in a turn based game is no rocket science. You only need to supply two methods and find the optimal move from the resulting tree as described here.
If you want to see the described algorithm in action, download Laska from the Play Store.
Some of you might have read my article Android: Deploying multiple targets from one project. It describes how to create customized versions of the same software and therefore benefit from multiple apps with the same featureset. That deployment with an Ant script has proven to work well. For example our GMX Mail App is available in four different customizations, for different brands, and uses a similar approach with maven.
However, there is a better way now to handle multiple targets. It is less complex and gives you even more options to customize the different targets. By using an Android Library Project, you still have the benefit of sharing resources and code, without the hacky Ant script. Remember, the Ant script would go through every Java source file and change an import statement, just to resolve the different package name of the R File. Switching between Targets required an Ant build with a refresh of the workspace. Not any more. Now switching between projects is as simple as clicking the run button in Eclipse. Especially for bigger projects this is a huge benefit, because refreshing the workspace can take quite some time.
So what is the new setup? You need to create one base project, with everything common inside, and declare it as an Android Library Project. This option is available under project properties in the Android tab. Then you create the first one of your targets as a different Android Project in Eclipse. On the same properties tab of the new project, you add the base project as a library. Repeat it for another project, which will be your second target. Now you will have somthing similar to this:
For the showcase, I deleted all source files of the custom projects. Since we want to re-use the majority of our sourcecode from the base project, we don’t need any custom-sources right now. There is one little fix we need to apply to the AndroidManifest.xml file. The Android Wizard in Eclipse uses relative references to our Activities. This does not work if we want to use our Activity from the base project, because it uses a different package than our custom project. Therefore, we have to specify the full package name to the specific Activity. In my sample the important part looks like this:
That’s it. You can now overwrite all your base resources and source files in the custom projects. Every new feature developed in the base project is immediately available everywhere. Only if there is a need for updating the AndroidManifest, you have to edit it in all custom projects. But this also means you have a fine grained control over the manifest file.
I updated my old example project on Google Code. Feel free to use it as a start for your own project with multiple targets. Feedback and contributions are always welcome.
The Android AppRater is a little tool in form of a source code snippet for getting better ratings in the Android Market. Its basic idea is to kindly ask users to rate your application, after they have been using it for a while. Which is a fair deal, because many users only give negative ratings right before uninstalling an application. This way, you only ask those users that are actively using your application.
For my own Android game Laska (light-version) the AppRater is not yet integrated in the current Market version. But it will be part of the next update. The dialog will only be shown if the app is installed for more than three days and has been launched at least seven times. And if you dismiss the dialog, it will not pop up again. This is what it looks like:
Of course, if the application is a piece of crap, this doesn’t help in any way. But for all other apps, it might be worth the effort.
The Free Lunch Is Over – that is the famous headline of an article about how the evolution of hardware alone will not solve our performance problems anymore. With the rise of multicore CPUs, software developers have to put effort into their code, to see further performance gains. Even on mobile phones, as the Tegra 2 shows, we will see more and more multicore CPUs. Therefore, there is a good reason for using concurrency in your application. However, it can also be dangerous and lead to unpredictable errors.
The following is a talk I have given to co-workers about concurrency in general and how to do it in java. It focuses less on academic problems like deadlocks, but shows how painful multi-threading is with the standard library. And there are some best-practices for how to reduce the likeliness of errors.
Update: This way of deploying multiple targets is considered outdated. There is a better way now.
This posting is about how to create multiple versions of your Android application without cloning the whole project. For example if you want to create a full (paid) app, as well as a lite (free) version of it, you might want to automate the task of switching between them. Both versions should be able to use different graphics, different strings and even different featuresets.
First of all, what causes trouble with multiple targets on Android is the auto-generated source code and the strict checking of Java. Strings and graphics are all kept in one place, namely the res folder. Simply creating one res folder for each target and switch between those folders solves the problem with all resource files. I will give an example Ant-script for this later on.
So, having different resource files seems easy. But there is one more problem. We want to have two different applications, so both targets don’t replace each other on our phone. Meaning, the targets need a unique package name in the AndroidManifest.xml. And it’s getting worse. When changing your applications package name, you also change the package of the automatically generated R file. This R file usually is referenced in a lot of source files – basically everywhere you need graphics, strings or other resources. So you end up editing a great amount of your sourcefiles when changing the application package name.
What is the trick over here? Well, I don’t have any. My approach goes through every Java file and changes the import statement for the R file:
There are several targets in this Ant-script. The default target is myproject, the others depend on the default target (for example myproject is always executed before the otherResources target). myproject does the following:
deletes the current res folder
copies its own customized resources into res
replaces all ocurrences of “import com.android.multiple_targets(.*).R;” with “import com.android.multiple_targets.R;”. This might be necessary if the iamdifferent target changed it to “import com.android.multiple_targets.iamdifferent.R;” for example.
the package name in the AndroidManifest is changed to the target name
Just execute a target to switch to this version of your app. You need to refresh the project in order to reflect the changes. In Eclipse, this can automatically be activated by setting the following option:
One more thing that does not work out of the box is the android:name parameter in activity declarations of the AndroidManifest. When auto-generated, they are declared relative to the project package. This doesn’t work anymore when the package name is changed. Therefore you have to set the activity name with absolute values. Instead of