Hi - I just discovered Vassal! Wow!. I have Opus 4.8 looking at it and it seems to have a method for creating an AI opponent: moving counters, launching functions like attacking, etc, and of course Opus can become an expert at the game in a few moments by reading the manual, rules, and strat guides. I presume when Fable 5 returns, it will be almost trivial to create byte-perfect methods to have AI play as the opponent, and probably with a single shot prompt like “learn to play Arnhem in Vassal”. Just wondering if anyone else has already gone down this road? I saw somewhere that V4 will help in this endeavor, but I presume Opus 4.8 or Fable can just builds it own MCP without need of an API.
Specifically, V4 will have a convenient API for accessing game data, usable for any purpose.
The topic of using AI - or rather Artificial Neural Networks (ANN) - with Vassal keeps popping up in the forum, so there’s dedicated Documentation page for it: see Does Vassal support computer opponents?.
Read the above linked post. What the ANN will need is access to game state, etc.. which is most effectively done through an API.
Begin Rant
That said, remember what it is an ANN does: It recognises patterns and then replicates those based on current input. To recognise a pattern, the ANN needs to have seen that pattern, or something very similar, beforehand.
What this means is, that an ANN opponent would need to train on previous game patterns - e.g., logs of previous game session. And to be any good at recognising anything, the ANN needs to have seen many examples. Those a generally not available.
Do not be fooled by the apparent ability of some ANNs to come up with “new stuff” - it doesn’t, because everything is based on what it has seen before. A"I"s are not intelligent in any sense of that word - at least, not more intelligent than a thermostat.
That said, it is not like ANNs does not have their use - you just cannot rely on it to do anything remotely intelligent, safe, or correct. ANNs will like try to cheat if it fits with a pattern it has recognised. Remember, the ANN does not understand the rules - it just recognises a pattern.
Some people use ANNs to “read” the rules for them, to make summaries or do a quick query. However, ANNs do fail to accurately do that job too (“yes, you can move your division through your opponents division”, “no, you cannot disengage from combat”, …).
End Rant
Yours,
Christian
Impressive. Perhaps you can share your set-up and how you got it to work like that. Perhaps as a Git repo (GitHub, GitLab) so that others may try it out on their favorite game?
Yours,
Christian
- Just because the purveyers call something artificial intelligence doesn’t mean that there’s even a shred of actual intelligence.
-
- Why did Sam Altman and several other heads of “AI” companies suddenly fled to Argentina?
- As a computer engineer, I can tell you this – what’s called “artificial intelligence” are nothing more than regurgitation automatons.
- AI depends on random-number driven guessing methods, called “hueristics.” There’s a saying in the AI community: “Hueristics are NOT reliable, because if they were, they would be called algorithms.”
Notice all of the “management bros” who fired thousands of workers a few months ago, dreaming that they could run entire companies by just typing LLM, with zero white collar workers on payroll, are now DESPERATELY trying to rehire the people who they fired, now that LLMs have just proven to be an extremely expensive way to completely screw up a company…
What’s called AI is NOT AT ALL like the hype you’ve heard.
I asked various LLM’s to do a simple, well-defined task
Write a C compiler for a Motorola 6809 8/16-bit microprocessor. It’s data handling capabilities are far better than the Intel 8088 and 8086, let alone the Intel 8080, 8085 and the various varieties of the Mostek 6502.
All of the LLM’s can give me complete C compilers for any of those other microprocessors, but NOT for the 6809, despite the fact that writing a C compiler for the 6809 is actually EASIER than for any of those other chips.
Why?
Because there’s no source code for a 6809 C compiler for any of the LLMs to cheat off of (in the sense of a student taking a test looking at his neighbors on both sides to get the answers, because he himself is clueless).
That being said, if you want to try it, knock yourself out.
Just be prepared for a really high amount of effort and data examples before you start seeing anything that’s worthy of playing against.
So, being a noob here, I should explain that this is not really a fork of Vassal.
TLDR: What I did was prompt Fable to “impose movement rules on a game running in Vassal” and then “allow me and you (the AI opponent) to move pieces on the board” Thats it. Fable determined that the save file (.vsav) could be used to track state, then it created an api and a Vassal-look-alike GUI.
Full description:
The tech stack, layer by layer (all of it is Python — no Java, no VASSAL modifications, no mouse-clicking automation):
-
The save-file codec. A VASSAL .vsav file is secretly just a ZIP archive. Inside it, the actual game state is lightly
obfuscated — a header called !VCSK followed by the data scrambled with a simple XOR cipher (a one-byte key, 0xA3 for this
module). We wrote a decoder that unscrambles it into plain readable text and re-scrambles it byte-perfectly, so VASSAL can’t
tell the difference between our saves and its own. -
The board parser. The decoded text is a long list of records — one per counter (with its pixel position on the map) plus
separate “stack” records that control what actually renders on screen. The subtle bug that cost us two failed attempts: a
unit’s position lives in both places, and you must update both or the counter doesn’t move. Our parser reads all 99
counters. -
The hex math. VASSAL stores positions in pixels; wargamers think in hex numbers. From the module’s own configuration we
pulled the grid geometry (96 pixels wide, 119 tall, staggered columns) and derived pixel<->hex conversion formulas, then
validated them against seven known setup positions from the game’s player-aid cards. -
The terrain and rules engine. We extracted terrain for all 1,128 hexes from the map image itself — color-classifying
every hex and hexside to find towns, woods, rivers, roads, and all 14 bridges (which landed at the historically correct
spots). Movement costs came from the map’s own printed terrain key, and the movement/zone-of-control rules from the actual
scanned SPI rulebooks. Legal movement is computed with Dijkstra pathfinding (a standard shortest-path algorithm): road
bonuses, river crossings blocked except at bridges, zone-of-control stops, all of it. -
The move writer + clients. To make a move, the engine edits the decoded save, re-encodes it, and VASSAL simply reloads
the file — the counter is now where the engine put it. On top of that sits a local web UI (a small Python HTTP server plus
an HTML page) that shows the real scanned map and counter art pulled out of the module file, highlights legal destinations
in green, and lets you drag counters — plus a watcher script that detects moves a human made in VASSAL and rules them legal
or illegal.
I guess what I’d request from the community is a really simple scenario from a well-known game (Panzer Leader?? Squad Leader??) to base a complete prototype on. It needs to be well-known so testers who are familiar with it will detect errors in the design, and eventually will lose to an AI that plays “perfectly”, or maybe cheats.
Large parts of your code can be replaced with pywargame, which not only can decode/encode the .vsav files, but also decode/encode .vmod, without explicit assumptions about the module, saves, stack sizes, and so on.
What pywargame doesn’t do is
- Extract terrain from images. That will always be very dependent on the graphics used. What I’ve done in some of my modules is to read the terrain off by hand (including river hex sides), and then store that in Global Properties.
- Calculate movement cost. Again, very game specific. However,
pywargamedoes allow you to get information from the module , such as grids and similar.
What it does do, is
- Fully decode and encode traits and state of pieces saved in
.vsavor.vlog - Fully decode components of the module, extension, etc.
What I see in your code above, is essentially
- A daemon that monitors one or more
.vsavfiles, and reads them in when they change.- Reads in current positions and compares them to previous positions.
- Then uses hard-coded rules to see of a move is valid
- Then updates a display or some other feedback
(sort of what I proposed here, with a little sugar on top in terms of the HTTP server).
What I do not see, is how the ANN interacts with this, nor how it will take decisions on what to do.
Yours,
Christian
No AI opponent yet. This is just the scaffolding: a movement engine, combat engine, UI. The I’ll add the AI opponent and human player’s attache. Since Claude wrote the API, a companion MCP is a trivial bolt on. So, in the end, you could just tell Claude “move the 508 Regiment to Nijmegen”, and then “make the axis of attack hex 3215 south of Arnhem, and first capture the local bridges”.
And…Fair point on the save-file layer — pywargame covers reading/writing VASSAL files, and it’s good work. But that’s ~2 of our 15 files. The rest of vsav-engine is what pywargame doesn’t attempt: a rules engine (terrain-aware movement, ZOC, CRT), legality judging, a board UI, and an AI driver. Chris Brooks’ Italy '43 experiment showed why that layer matters: pywargame + an LLM proposing moves directly produced illegal ZOC-violating play, and his takeaway was that you need a coded move generator. Agreed — so we built one.
Actually, most of your code - by lines - is reading in the Vassal data from save or module files, and I’m afraid that your code is very specific to your use-case. The path-finding, terrain-detection, and similar code is not a large part of your code, and most of those parts will always be specific to the use-case.
Rules handling will always have to be specific to game at hand. pywargame does not attempt anything of that nature.
I don’t see the AI driver anywhere.
I’m not aware of “Chris Brooks’ Italy '43 experiment” - could you elaborate?
pywargame does not deal with moves or the like at all, so if the experiment failed, then it must be in the LLM part.
To check if a move is legal isn’t all that hard, and can easily be coded in “regular” code. Assuming
- You know where every piece is on the map (
pywargamewill tell you that from reading a.vsavor.vlog) - What the terrain and features of all hexes and hex-edges are (
pywargamedoes not tell you that - that has to be given by an “expert system”) - What the capabilities of each piece are - e.g., movement factors (
pywargamemay tell you that, if the module designer encoded that into the module - I do that often). - What the “cost” is to move from one hex to another
- Which hexes, or other possible path ways, exists.
then its a simple matter of stepping through possible moves until there are no longer any more possible moves. There are algorithms designed for that - such as A* or - as you mentioned - Dijkstra’s algorithm
.
For example, the unit A has MF=2.
- To the north is a woods hex, cost 2 MF to enter, so the first candidate path is a single move to the north.
- To the north west is a clear hex (1 MF), and beyond that are 4 clear hexes (the one to the north east of that hex is woods, remember)
- In the north hex of the north has an enemy unit in it, so the cost of entering that hex is infinite.
- That leaves 3 other clear hexes where the unit
amay enter because it has enough MFs to do so.
In fact, for most games, a simple, brute force, algorithm will probably be the most effective.
What I’d like to see in Vassal 4, is to have a clear distinction between client and backend. The client is what the “user” - which could be a computer opponent - interacts with. The client sends requests to the back-end which then checks things and sends back a reply the client with the updated state. Other clients will get the same update as needed.
The client can be somewhat generic. Sure, some requests will be specific to a game, but that can be handled in a generic fashion (much like it is done in Vassal 3). The back-end is where the rule checking, path calculations, etc. is taking place, and that will be game specific. That’s why we need the back-end to be highly scriptable.
For example, a user selects a piece in the “Movement phase”. The client sends the request piece foo requests possible moves. The back-end replies here's a list of possible moves for piece foo. The client can then render those possible moves. Or, client sends the request, pieces a, b, c, and d would like to attack piece A, and the server sees that d isn’t adjacent to A and can therefore not attack piece A, so the server responds no can do, d isn't adjacent to A.
Practically, I imagine both the clients and back-end reads in the same data from a module. However, the client will only deal with those parts that has to do with graphics and visualisation (map image, grid, piece images) and possible requests (e.g., context menu items). The back-end focuses on issues such as grids, pieces capabilities, rules, etc.
Yours,
Christian
Have you been using fable? My arnhem example is just a technical demo. Fable can likely do that with any game that can be played in vassal. I would guess that after it does 2 or 3 games it will decide on its own to create unified game-agnostic version. If I get a chance I’ll have it do that tomorrow’s. “fable, please create a generalized approach to move adjudication in vassal”
How about the prompt
- Write a custom component for Vassal - in Java - that puts all commands on a stream e..g., a socket.
- Then expand
pywargameto handle all possible commands - Then write a client
- Reads in a module, using
pywargame - Reads the commands from the stream, using
pywargame - Calls user supplied Python script with relevant information to validate commands
- Possibly sends commands back on a reverse stream
- The client should possibly also provide a GUI to monitor the activities, including rendering game graphics and moves, commands, and other changes
- Reads in a module, using
Or, perhaps, simply
- Rewrite Vassal
- In a platform agnostic environment, e.g., using Python and PyQt. Should also run on mobile devices
- Separates client from back-end code
- Stores game state in back-end and client can query that game state
- Makes the back-end code highly scriptable
- Uses commnuication standards and open formats for storage and representations
If that pans out, then that would be a start for a new version of Vassal.
Yours,
Christian
I’m travelling right now so don’t have time for a thorough reply., but wanted to comment on this in particular: There are games where returning explicit move lists won’t work due to being too long. If you’re designing requiring a move list, you’re going to hit this problem.
I am far into developing a new board game engine. One of the main concerns I have is security. This includes preventing your opponent from accessing information he should not have. A good example is a game with cards where you don’t want your opponent to see your hand. And yet this information must be stored when you save a game. I am afraid that making all information available (through an API) will be used to reveal information. This can be used for cheating.
Making a computer opponent is certainly interesting as a separate project. But an engine made to play against humans should have very restricted access to information.
Besides, as a developer, I agree that the much of what is called AI is hype. AI deals with data. Humans deal with reality.
What is communicated between the back-end and the client should be entirely configurable. If communicating a possible move list is untenable, then the module should refrain from doing so.
In such cases, a better way of handling moves would be to let the client do the move, then have the back-end check it, and if the move was illegal, move the piece(s) back and notify the client.
The point is that the engine it self should not dictate any of this, but should allow for various ways of doing such things. That’s why the back-end needs to be highly scriptable and have introspection on the engine. Forcing a one-size-fits-all on modules would be counter-productive.
Of course, this means that some of the burden is pushed to the module developers, which could be leviated through standard components - e.g.,
- one component that takes a piece position, information about a board, and the game state, and calculates possible moves (
possibleMoves(piece,board,state)) - one component that takes a piece move, information about a board, and the game, and then checks if the move was legal (
validateMove(piece,start,end,board,state))
Both components would make customisable call-backs to the back-end to get for example movement costs, piece capabilities, and so on. Again, such things could be made simpler by standard components. For example, the back-end sets a table of movement costs and registers terrain, features, etc., and then a relative standard component can calculate costs based on that information.
Yours,
Christian
@Rhett I have considered this too. Yes, my system should allow fog of war which is impossible when you have a physical board on a table with players right there. And yes, the intel available to the AI opponent can be titrated. At base competency level it knows what he human knows. At epic level, it knows everything and always cheats to roll the best possible roll, and everything in between is possible Fable is creating the general move model now that streams in a games movement rules and applies them as a custom set applicable to a specific module.
Not sure I understand all this but I think I can deliver: 1. download module; 2. Fable-app finds movement rule stream; 3. Apply unique movement rules for the game, add that game to Fable’s knowledge; 4. Render in my little html vassal-look alike.
Oh, maybe I understand. The “possible moves” are all clearly documented for human players to follow. If its written somewhere, Fable will find it and implement it to perfection. I suspect Fable will find many internally inconsistent rules that were always there but unknown to the game developers themselves. That the whole cybersecurity story with Mythos.
OK, just created the general model and it worked for Tobruk - but I dont have a Tobruk rulebook, so I made up movement rules. Note Tobruk also has unit facing which is fully implemented (right click to change facing). Next is the stress test: ASL using the VASL module: more complex yes, but more likely to be fully documented. Will do ASL then release to you a tech demo of these 3 games.
Dr Evil’s Move Legality Engine for VASSAL — tech demo. This is an early, in-development proof of concept, not a finished product — I’m sharing it so people can see where this is heading.
Three classic hex wargames (SPI Arnhem, AH Tobruk, ASL) with movement rules fully enforced, running entirely in your browser. Unzip, double-click START_HERE.html, drag counters — the engine computes every legal destination: terrain costs, rivers and bridges, zones of control, walls and hedges, vehicle facing. No install, nothing to configure. Expect rough edges
— each game is one scenario, movement only, and some unit values are still provisional.
Download: https://drive.google.com/file/d/1Gxjkx3myzGKjuww4NgXyanX9Ld3jylnI/view?usp=drive_link
Source (MIT): GitHub - DrEvil-TitaniumHelix/vsav-engine: Game-state API for VASSAL 3: read, rule-check, and write .vsav save files directly. No VASSAL modifications. By DrEvil / Titanium Helix. · GitHub
This is step one toward the real goal: an AI opponent for VASSAL games. The full engine reads and writes real VASSAL .vsav files — so VASSAL, this browser UI, and an AI player become three interchangeable clients of the same save. Move legality was the foundation; combat resolution and the AI itself are next.
