Forums

Let's write a Chess GUI from scratch in Java

Sort:
watcha

The following post will be of zero interest to the wider public, it will be too technical. Why do it anyway? Well, I found that having to write them down publicly helps me to better organize my thoughts.

Now, down to Java programming.

Some actual shortcomings of Java widgets.

For example suppose that you have a slider. If this slider is moved and you want to know about this, you just add a change listener. Sounds easy. But what happens when you have a dozen sliders? You are tempted to create an array of them, and have only one event handler, which is indexed by the array index. But unfortunately in Java the source of the event for a slider cannot be established within the change listener. It can only be done for widgets which have action events ( since the action event carries information as to its source ). You are left with writing change listeners for every single slider by hand.

MyGuiBuilder solves this problem by having a MySlider class which has an identifier (id) as its member variables. This MySlider class has an actual Slider as its Java node element and within the change listener added to this actual Slider, the id is visible. So whenever the slider is moved, the event handler is aware of the id of the slider that was moved.

An other actual problem is that a Java ComboBox turns out to be buggy when you want to delete an element from its list ( this is an actual bug reported on StackExchange ). The workaround is to delete the widget from its parent's children list and recreate it from scratch with the new list. MyComboBox class implements this behaviour, so it creates a flawless CombBox which works as expected.

But not only that. The main point of having derived widgets is that they can have identity and built in behaviours. In the script all MyWidgets have a human readable id. The value of the widget ( text in a texfield, selected element of a combo, true or false state of a checkbox etc. ) is automatically stored in a hash table where the key is this id. On startup the hash table is loaded from disk, on application shut down it is saved back ( in the overridden application start and stop method respectively ). So the state of all widgets is automatically backed up and is available during the run as a hash.

The fact that widgets now are not stored in Java global variables but are accessible through string id-s is very useful.

MyGuiBuilder has a concept of a unified event handler. All MyWidgets send there events to one single function. You have to override this event handler in the actual application. Suppose that you want to handle events which change the color of some elemt of the board ( piece color, square color etc ). Now that id-s are string, you don't have to write down and endless if statement to pick out the events which try to set a color. You can simply pattern match the id, and it it contains the literal "_color" then you know that you have to redraw the board with this new color. You can write something like:

if(ev.id.contains("_color")) { draw board... }

No declaration of variables, no configuration of widgets in separate statements, no writing of event handlers by hand for every single widget and this is just a beginning.

You can define complex behaviours which requires the interaction of widgets.

For example I have a MyProfile widget, which orchestrates together the behaviour of a combo box, a textfield and two buttons and arbitrary additional settables widgets. Together these form an editable profile. When you write something in the profile name ( which is the user name in the above example ) and press the Add/modify button, the profile having the values of all the additional setting elements will be saved under this name and will be listed in the respective profile combo ( so that you can later select it ). The delete button deletes the current profile from the list.

All this behaviour is automatic, so adding a new setting profile or elements to an existing profile is a matter of simple scripting. The same is a pain when you have to do it in plain Java code.

Also the sript allows you to have macros.

The fact that the main window and the settings modal window have the same rounded border is achieved by a macro bvbox ( bordered vbox ).

This complex behaviour is then simply invoked by the macro name in the actual script as !bvbox wherever it is needed:

The script can refer to hash values by id-s ( in the form $id ) so the widgets are automatically configured to the latest settings when they are built.

As you can notice the script language is as simple as possible, all small cap letters, not quotation marks, no commas, semicolons, brackets, etc.

This is intentional. It has to be as simple as possible, so that you can write new code quickly and easily.

watcha

A GUI can be ultimately thought of as a tree of nodes. Nodes are either visible GUI elements ( buttons, sliders, textfields etc. ), or holders of further GUI elements as their children ( layout nodes like grids, vertical boxes, horizontal boxes etc. ). If you want to describe this you need a data structure where nodes are hashes of properties ( the width of a textfield, the caption of a button etc. ) and their children are array of nodes. The natural problem in GUI scripting is how to describe this nested structure of hashes and arrays as a single string so that it can be later reconstructed from this string - a method called 'serialization'.

 

Java has a powerful method for serializing data structures, but the problem is that it produces binary output which is not human readable. There are human readable serialization formats like XML and Json. Why not use those? Well, they are an overkill: they are way too complicated. I need something that I can write on a Notepad by hand.

 

I developed an own serialization format for arbitrary nested structure of hashes and arrays ( it can contain arrays of hashes, hashes of arrays in any combination and depth ) with terminal scalars ( ultimate hash values or array values ) being strings ( this is not a real constraint, for it is very easy to store integers, Booleans, Doubles etc. as strings and parse them back when needed ).

 

This format is suitable to describe an arbitrary GUI structure, but still this is too rigid and abstract. The script language can take into to account the specialities of a GUI and make further simplifications and be more human readable.

 

So learning the lessons of my first rough shot at GUI srcipting I decided to have a 4 step process.

 

1. Describe the GUI as a script ( human readable ).

 

2. Translate this script into hash/array serialization format ( still human readable ).

 

3. Translate ( 'deserialize' ) this serialized format into Java data structures ( not yet GUI structures! ).

 

4. Ultimately when everything is in Java data structures, parse this structure and build the actual GUI elements and their children in the process.

 

The advantage of this among others that where the scripting language has not enough expressive power due to its simplicity, you can always insert native serialized parts in it as escape sequences. So you have to be complicated only when it is really necessary and can be simple most of the time. For example the scripting language is only prepared to store simple key-value pairs as node properties. But a combo's value is more complicated than a single key-value pair. It consists of the selected option and also the list of possible options ( which can also change, since the options of combos selecting editable profiles are edited by the app during the run ). Since the application stores any change to the value of GUI elements on disk ( and for this purpose it looks for the 'value' property in the property hash of the node ), this value has to be under a single key 'value' which is a hash of a string and an array in this case. If you want to give a default value to a combo in the script, you have to describe this structure somehow and now you can fall back to the serialization langauge and insert an escaped serialization sequence with the appropriate curly brackets and square brackets ( which are to be avoided in the script otherwise, for it has to use as little number of special characters as possible ).

 

Besides the inablity to store nested values, the other shortcoming of the first version was that with editable profiles you had to explicitly list all the elements the belong to the profile which was an ad hoc solution.

 

In the new version you can define a profile parent node and all children of that node that can have editable values will be detected during the run and their change in value will be reflected on disk. So to add a new option to an option list you just add a new element in the script within scope of the root node and all other things are taken care of by the app. The same is true for special controls of profiles ( add button, delete button, select combo ): whenever any of these are clicked, the profile that has to be edited is automatically detected whith traversing back the tree to the root of the profile and detecting the id of the profile that needs to be edited.

 

The script describing the chess board:

 

 

 

The way the values are stored on disk:

 

 

 

The way how the whole thing looks when run:

 

watcha

Finally MyGuiBuilder is public:

 

https://github.com/myguibuilder/myguibuilder

 

As an example it implements a very basic chess GUI ( a board with adjustable colors and size, which can list the legal moves in the position and you can make moves by drag and drop move input ). This is admittedly much less than what javachessgui and javachessgui2 can do, but the main goal here is to make public the interpreter for the new scripting language.

In fact you only need very simple text editor and start writing a GUI application in this language without writing a single line of Java code or having to run a Java compiler or an IDE.

This is an interpreted language and the interpreter is a Java program which you can run by double clicking on the 'Gui2.jar' file in the 'dist' directory of the distribution ( of course you need the latest version of Java installed on your computer as a prerequisite ).

The files that are at the heart of this program are what I call gui descriptors. These are files that describe the GUI application as human readable and editable text files. When the program is run it copies all the decriptor files that are invoked during the run ( that is: all the files that belong to the windows actually opened by the user and their dependencies ) into its working directory as text files with the extension 'txt' ( so that they can be readily opened in a text editor ).

The program starts up from the file 'main.txt'.

Here comes your chance to start programming in this language: you can edit the 'main.txt' file with a text editor, and on the next run the program will start up from this edited file.

Of course the descriptors files are programs, so if you mess them up, it will crash. But no problem: if you delete all the buggy text files from the working directory which cause trouble, on the next run the program will replace them with the original correct ones, so you can start over again.

There is no manual for this language, but looking at the files someone with a little interest in programming can guess how they work.

For example if a line starts with '!' this means that the descriptor file having the name following the '!' mark should be substituted here literally, this works the same way as #include in C/C++.

watcha

Now my new approach is beginning to pay off.

I added an engine setup panel which allows you to set up several engines in such a way that was impossible using the old techniques.

The settings for a single engine are stored in a template file:

Then I can invoke this template number of engines times with a parameterized include statement ( with the repetition count being set to the number of engines ):

The template file contains the special sequence #$ at every place where the template is dependent on the index ( for example labels saying "Engine 1 path", "Engine 2 path", "Engine 3 path" appear as a generic label "Engine #$+1 path" in the template file ).

This is not only useful in auto generating the whole panel, but has very high added value when it comes to event handling.

Since the id-s are also indexed and the event handler is smart enough to deduce the trunk of the id ( which I call 'true id' ) and the index at the end of the id and fires an event which contains this information.

When you have to handle the event, you just query the true id ( ie. 'install_engine' and call the install_engine(int index) procedure with the index stored in the event, that's all ):

This is impossible in plain Java, because you cannot create and pattern match Java variable names like strings.

If you want to do a similar repetitive panel in Java, you have to create a class that can instantiate one block of the panel and then create an array of these blocks and search for events by looking up event sources in arrays, just thinking about it I'm already tired. I know how much work it takes. This is why I gave up the development of javachessgui. It was a pain to add a new complicated element, and even if you added one, you had to start the hard work all over again, since creating one element was no help in creating an other.

I identified all the repetitive redundant tasks and try to make them automatic in MyGuiBuilder.

This engine setup panel is my first true achievement in this new regime.

watcha

After the engines setup actual engine analysis was added - now with multiple engines. I still don't like engine output panels, so every important information has to appear on the board.

With multiple engines it looks like this ( the main engine is color red and its suggested move Nbd2 not only has an arrow, but it is highlighted, so that you know which move will be made if you press the make analyzed move button ):

Thomas9400

JAVA SUCKS. but i see no alteritinve unless you wait 10+ years for python to overtake java

watcha

On some level you can say that Java is a bit tardy. You need to do too much coding to get some result. ( Although if you are looking for an interesting alternative there is a language called Scala, which also runs on the JVM, it combines object oriented features with functional programming and built in pattern matching, so that it can produce very compact code. Scala at the same time is fully compatible with Java, it can import Java libraries(!), so you have all the existing functionality of Java. Interestingly enough the second largest chess site is written in Scala. I don't yet use Scala because its infrastructure is not as developed as of Java. )

My scripting language is in fact an effort to make use of the good GUI capabilites in JavaFX, the portability and speed advantages of the JVM ( Java Virtual Machine ), the very advanced NetBeans GUI and at the same time not having to do the much complicated coding that Java requires.

I do the complicated stuff once, and from then on I have only the advantages, because I can do 90% of the hard work in the script language, which is simple or at least works as I expect a language to work.

DiogenesDue

Java is out of favor ;)...if you want the Unix sysadmin types (who more than likely suck at development) to not make sarcastic remarks, you should probably use Ruby on Rails or something.  Or, you could just ignore people with nothing useful to contribute...

watcha

If by 'out of favor' you mean 'first':

DiogenesDue

Yes, out of favor.  Note the steady decline.  Just because Java is currently in an uptick does not mean it isn't still falling out of favor.  It's been that way ever since Sun got bought out by Oracle.  The client is considered a security risk by many, etc.

I was developing Java systems pre-Swing ;), and I championed it's first use at the company I was working for at the time...so it's not like I am a Java-hater.  It's just reality.

As for the security snafus...if you develop your GUI client on Java, just be prepared for the fact that there are a very significant portion of the public that will not use (or even download) your client.  I have not had a JRE installed on any of my PCs for quite a while now, for that reason.

Case in point:  chess.com's sister site, gambit.com, has this tagline:

NO FLASH. NO JAVA. NO DOWNLOADS

watcha

If I'm not trusted, no one will download and run an executable created by me. This is independent of the language the executable happens to be written in. I can buy the professional version of Microsoft Visual Studio for thousands of dollars, create a state of the art native Windows GUI application, and no one will download it and run as far as they don't trust me.

 

Anyway I do this for my own pleasure, if there are zero downloads I'm still happy with using my program, still GitHub is a nice backup facility, I can upload the repository with a single click, as this takes only running a git script batch file, while in Google Drive ( which I also use as a backup ), you have to log in and click all your way to uploading a file.

watcha

Now, down to development:

 

By now I managed to import all the functionaly javachessgui2 had to the new framework.

 

So now you can use myguibuilder as a fully featured chess GUI:

 

https://github.com/myguibuilder/myguibuilder

 

Some features:

 

- variant support

 

The program has the capability of supporting different variants. Currently the only supported variant is Atomic. Atomic chess is a game with very deep and unexplored theory.

 

- book

 

In the book you can annotate moves. Each position is stored in a different file under a name which is the base 32 encoded version of the trunk of the fen ( without move counts ) so transpositions are handled correctly. Of course each variant has an own book. The moves are color coded and sorted according to the annotation. If two moves are equally annotated, the ordering follows the stored engine evaluation, if this is also equal then the number of times the move was played within the GUI.

 

- deep analysis

 

With one click you can evaluate all legal moves in a position. The evaluations become part of the book. This way you can get a quick overview of the position, which moves are worthy of exploring further.

 

- game annotation

 

You can with one click annotate a game for white or black. This means that all moves of the given side in the game are annotated with the minimum 'recommended' level annotation ?! ( if they had better annotation earlier, this remains that way ).

DiogenesDue

Actually, I would have trusted you ;)...but not a Java client.

watcha

With the new scripting method I can develop features so quickly, that I now have energy to listen to appearance.

I have added three skins to the application:

Wood

Marble

Rock

Rock looks awful, but anyway, you can have a rock board.

watcha

From the outset it was my goal to make the application as flexible as possible and have as little amount of hard coded ( especially Java hard coded ) features as possible. Already in the beginning the files describing the layout of the application were provided as editable text files so that you can change the layout using the script language without writing a single line of Java code or having to use an IDE or compiler.

Now I have taken this a step further. Indeed all resource files the application has are now presented to the user. I introduced a new widget called dcombo ( directory combo ) which is a combo that always reflects the contents of a directory and you can select a file from that directory with it. Dcombo allows the user to upload images into resource folders, so that next time when the application is run or a settings window is opened, the respective combos take their options from this directory and will reflect the new file uploaded by the user.

For example the application lets you to have a background image of the board of great chess players. As a default Kasparov, Fischer and Capablance are offered.

Here is a board with Capablanca background:

However it is appreciated if you have other ideas about who the greatest players are.

Suppose you are a Nakamura fan. You can go ahead and upload an image of Nakamura in the resources\backgroundimages directory. When this is done the dcombo in the board settings panel will offer Nakamura as an option:

And there you go, Nakamura is on your board:

The same is true for every resource, so for example if you don't like the button icons or chess fonts provided by the application, you can change them.

watcha

The new control panel:

I just can't get right the transparency of the button icons.

kiloNewton

i'll learn java

fburton

This is all extremely instructive - thank you, watcha.

watcha

What is really instrutive is how you change the background of a button icon to transparent.

I thought this must take two or three clicks just I have to do my research.

This is only half true: I have to do my research but it takes more than two or three clicks...

I'm ten years older now then when I started finding this out, it is so complicated. Nevertheless I found it out.

This is how you go:

First you load the bitmap of the icon to Open Office Draw:

So far so good.

Now, Open Office has a function which changes some specified color of an image to tranparent. I somehow have to paint the outside ot the round icon to some color that is different from those used within the icon.

This can only be done creating a shape with a round hole in it.

One starts off by drawing a rectangle and a circle in the middle of it:

There is a function which substracts one shape from an other. However you have to select multiple objects ( the one to be substacted from and the one to be substracted ). Here comes the problem: the circle is within the rectangle, so selecting it is not that easy. When you click on the rectangle, the selection of the circle disappears. In principle you can do this by holding down the shift key and then clicking on the circle, but the whole thing is easier said than done.

After half an hour of clicking you somehow select both objects and substract the circle:

Now you have a yellow outer region with a round hole in it.

You can drag the icon in the middle of the hole:

You naively think that you are done: you can apply the transparency tool to this object and you are done.

Nothing is further from the truth. You first need to convert the whole thing to a bitmap. But it turns out that bitmap is to rough, the outer region is messed up, it is not of a single color any more.

The only way out is to use the operation system's snipping tool on the image you can see in the Open Office editor, and then import this image and apply the transparency change to this image...

This is so complicated that I only managed to fix one icon so far:

Original:

Fixed:

watcha

Icons fixed: