It's been a while since the last release, and while there's not to much in the way of major changes there are a lot of tweaks on fixes. Most importantly the documentation is much improved - check out the "doc" folder for both an improved/updated manually and separate documentation for a lot of the different devices (as both Pages, and PDF's). This is still a work in progress, but it should make things a lot easier for everyone.
We've also made some tweaks to SniffPad that you'll appreciate if you're running on a laptop - its now a LOT less CPU intensive, which means it won't hog your battery, and it a little more responsive on slower machines.
When reworking the documentation we also made a few tweaks to the compiler - a few odds and ends had been missed, which should now work. We also changed the error message so instead of things like "syntax error" it now says things like "oops" and "I don't understand". Hopefully its a bit less intimidating (and clearer for people who don't actually know what a syntax error is yet!).
There's also the usual updates of devices and examples. We added support for cheer lights - just cause they're fun. There's a cheerlight demo for flotilla, plus code to bridge cheer light to MQTT.
Check it out on the downloads page.
Tuesday, 21 February 2017
Saturday, 18 February 2017
Girls Like Robots
I don't play a lot of games, but when I read about the Humble Bundle Freedom Bundle is seemed like an easy buy - lots of games and all the money goes to a good cause. So far the game that's taken up the most of my time is a little puzzler called "Girls Like Robots". It only takes a few hours to play through the whole game, but its a lot of fun.
The premise of the game is easy enough: place cards on the grid so that they all get along with their neighbours: Girls like Robots, and Robots like Girls. Nerds like girls (and robots) too, but they also like being on their own, so they like being on the edge of the board, and they don't like other Nerds. Of course Girls don't like Nerds (while Robots just ignore Nerds).
Here's a typical level (from around the point where its starts go get a little interesting). The grid is 4x3, and the level starts with a Nerd in the top left hand corner. We have to place 4 Girls, 4 Robots, and 3 more Nerds to score as many points as possible. Around 20 will pass the level, 22 to get a "Good" rating, and 26 is the perfect score.
It's the sort of problem that's really easy to solve with a little code - while we could do something clever, the easiest thing to do is just generate random placements and keep track of which is the best one. So lets give it a go!
The first problem is that Sniff only has a List which stores things in order - there's no easy way to obvious represent a 2D grid. We could make a list of strings, with each character representing a grid slot, and each string representing a row, but replacing individual letters would be fiddley.
Instead I made a list of numbers. Each number represents one square, along each row and then on to the next. We use a couple of other variables to store the height and width.
make arena list of numbers
make width number 4
make height number 3
make empty number 0
make girl number 1
make robot number 2
make nerd number 3
make edge number 99
Now we can just make up a number to represent each card type, and use that number but its going to be much clearer if we use a constant. I've declared empty, girl, robot, and nerd so I can put them in the list without having to remember what actual values I used. I've also declared edge, which will come in handly later.
when print
.make message string
.make counter number
.make card number
.set counter to 1
.repeat height
..set message to ""
..repeat width
...set card to item counter of arena
...if card=empty
....set message to join message "."
...if card=girl
....set message to join message "G"
...if card=robot
....set message to join message "R"
...if card=nerd
....set message to join message "N"
...change counter by 1
..say message
The first script I wrote was to print out the game status. We go along each of the rows, checking the square, and depending what it is we add a suitable letter to the message. When we reach the end of the line we print out the message and go around again.
The next thing I wrote was something to place a card somewhere in the grid:
make card number
when placeRandom
.make x number
.make y number
.make counter number
.forever
..set x to pick random 1 to width
..set y to pick random 1 to height
..#say join [x] join "," [y]
..set counter to (y-1)*width+x
..if item counter of arena =empty
...replace item counter of arena with card
...stop script
I generate a random x,y coordinate and then calculate the index of that square in the list. This is a bit tricky but basically each step in Y moves us down the grid one square, which moves us on by width steps through the list.
If the square us currently empty then we place the card in the square and we're done. Otherwise we go around again and try another square. With that in place its pretty trivial to fill up an entire grid with a random layout:
when fillArena
.broadcast makeEmpty and wait
.replace item 1 of arena with nerd
.repeat 4
..set card to girl
..broadcast placeRandom and wait
.repeat 4
..set card to robot
..broadcast placeRandom and wait
.repeat 3
..set card to nerd
..broadcast placeRandom and wait
Or at least almost random: As per the level above, I've placed a Nerd in the top left, then placed 4 girls, 4 robots and 3 nerds randomly. Of course I've been testing all of this as we go along using something like this:
when start
.repeat 10
..broadcast fillArena and wait
..broadcast calcScore and wait
..broadcast print and wait
..say ""
Which simply prints out 10 random card layouts.
Now for the hard part - we need to calculate a score for a card layout.
when calcScore
.make counter number
.make x number
.make y number
.set score to 0
.set counter to 1
.repeat height using y
..repeat width using x
...set card to item counter of arena
...
...if x>1
....set neighbour to item counter-1 of arena
...else
....set neighbour to edge
...broadcast calcCompatability and wait
...
...if x<width
....set neighbour to item counter+1 of arena
...else
....set neighbour to edge
...broadcast calcCompatability and wait
...
...if y>1
....set neighbour to item counter-width of arena
...else
....set neighbour to edge
...broadcast calcCompatability and wait
...
...if y<height
....set neighbour to item counter+width of arena
...else
....set neighbour to edge
...broadcast calcCompatability and wait
...
...change counter by 1
So to do that we go through the grid one square at a time, and calculate its happiness score. First we get the card thats in the square itself (item counter of arena). Then we check each of its neighbours starting with its left hand neighbour at counter-1. If course that won't work if we're on the left hand edge (x=1), so we check that x>1 and if its not we set the neighbour to be an edge (in other games we might be able to use empty, but remember Nerds like being on an edge).
Having set card and neighbour we then call calcCompatablity to actually work out that pairings contribution.
when calcCompatability
.if card=girl
..if neighbour=robot
...change score by 1
..if neighbour=nerd
...change score by -1
.
.if card=robot
..if neighbour=girl
...change score by 1
.
.if card=nerd
..if neighbour=girl
...change score by 1
..if neighbour=robot
...change score by 1
..if neighbour=edge
...change score by 1
..if neighbour=nerd
...change score by -1
This is where the "game logic" really lives. Here you can specifically see that girls like robots and don't like Nerds. As you can see Nerds are by far the most complex, as they have a reaction to pretty much every other card.
Finally all we have to do is generate lots of random layouts and see which is best:
make hiScore number
when start
.repeat 10000
..broadcast fillArena and wait
..broadcast calcScore and wait
..if score>hiScore
...set hiScore to score
...say [score]
...broadcast print and wait
...say ""
And when we do something strange happens... it suggests the solution:
NRNG
RGRG
NRGN
Which it thinks will score 27 points on a level where the "perfect" score is 26...
And there it is!!! 27 points!!!
In later levels we get to add in new cards - Pies (pie makes every one happy, except robots) and baby seals (everyone loves baby seals, but they hate girls), fish and cows. There's also the rule that robots can get too excited and panic if they're surrounded by 4 girls. Some of these are a bit more tricky to implement as they don't just apply to a single pair of cards, but with the framework in place its fairly simple to add these extra features.
Check out the Humble Bundle, and Girls Like Robots. They're almost as much fun as writing the solution!
Labels:
Algorithms,
Games,
Project
Subscribe to:
Posts (Atom)