mailto: blog -at- heyrick -dot- eu

Winter Solstice

It's the Winter Solstice today (at 23:23 CET), and very nearly a Full Moon. Well, the days will be starting to get longer, so it's the long slog towards summer and the good weather. Remember that when the wind changes direction and it's -5°C outside...

 

Advent calendars

Have you been watching my Advent Calendar openings? There's a YouTube playlist link on the right. Or, click here if you're using the mobile view...
There're only three days to go! And some of the stuff uncovered so far has been a little... odd... Many many flags and a moose - when's the last time you saw a moose at a hockey game? Plus the comedic value of Celebrations putting the tiniest chocolate into the biggest hole. I guess "cutbacks" or "it's the economy stupid"? ☺

 

Drone pests

So some idiot (idiots?) seem to think it's a good idea to clog up Gatwick airport with drones. The government wants to use this as an excuse to have all drones licenced (which will work about as well as CB licences) while the Independent showed a picture of a large quad-copter carrying a DSLR under it. If that was a real photo and not a Photoshop job, it indicates that the sort of foaming-at-the-mouth headlines as published in Bloomberg ("Your Favorite Holiday Gift Is a Danger to the Public", subheading "Drones in the hands of hobbyists are a menace") are likely deliberately missing the point.
No, Bloomberg, drones in the hands of assholes are a menace.
But, then, one could say the same for cars and knives and bricks...

There are three types of aircraft calling themselves drones. The first are little drones like mine. These are the typical Christmas present drone and they offer a range typically less than 100 metres. In other words, incapable of making it to the runway from any of the boundary gates of most airports.
The second type are the long range semi-pro models. These cost rather more and offer the ability to pilot them over several kilometres, these days often with FPV cameras so the operator can see from the perspective of the drone. These sorts of craft ought to already require a licence because there's a fairly good chance that it will not be piloted with a nearby line-of-sight from the operator at all times.
The final type of drone are the ones that contain an onboard computer (Arduino, Pi, something of that nature) and a GPS receiver, and they move themselves to programmed way-points by themselves. I have no issue with these requiring a licence, a beacon, and insurance - because they control themselves.

Now, what the authorities should do when there is a definitive sighting is to try to sniff out common control radio frequencies. If a drone can receive radio frequencies, so can a scanner. When the frequency is known, blitz it (and tough crap if it disconnects every mobile conversation in a five mile radius) until the drone falls out of the sky. Then disable the thing using whatever method is the most satisfying.

If there is no control signal, if this thing is following a pre-programmed trajectory, then that's getting close to an act of terrorism. People do not send an autonomous drone into the airspace of a major airport as an "oops, my bad". That's intentional and designed to create havoc, as indeed is happening.
Having said that, it's happened enough now that there's little way that we can think of it as anything either than intentional.

 

"Stupid woman"

+1

That's all.

 

Designing software

On the ROOL forum, somebody asked about the process of creating software.

It's a reasonable question. I mean, one could think that programmers are weird people that go into a trance, start speaking gibberish, and working software is the result.

The process of creating software starts with an idea. For the purposes of this, I shall talk about my Koi-Koi (Hanafuda) game. The first idea is a simple to-the-point description. "Koi-Koi for RISC OS".

The next step is to refine the idea into something that is a rough specification. In the case of Koi-Koi, this was simple because it is a card game with known rules. All we need to do is imagine it in a window on the desktop rather than actual cards on a table.

 

From here, it's time to start thinking about the implementation. In the case of Koi-Koi, we can immediately break the project into sections:

  • Arbitrator - deals with the game state: who is playing what and when.
  • CardHandler - deals with the cards: drawing a card, matching an on-screen location to a card, lagging what matches a specific card, looking up card info.
  • CompuPlayer - deals with the computer player: routines to provide the player with an opponent.
  • Configure - deals with program configuration reading, writing, and setting.
  • Player - deals with player actions: clicking stuff while a game is in progress.
  • Redraw - deals with the redraws: each part of the play area is redrawn separately so that we need only redraw specific sections (that which has changed, or that which is affected by a Wimp redraw request).
  • Table - deals with "table" actions: moving captured cards to the capture area, and autosorting.
  • WimpCode - deals with the Wimp: loading resources, attaching handlers, responding to events, and a lot of generic stuff that happens as a result of a Wimp action.
  • Wrapper - deals with starting the program: getting everything going in the right order, and the main polling loop.
  • Yaku - deals with Yaku detection: examines the cards in the capture area looking for yaku (and can record previous yaku).

Something like Arbitrator might not sound too hard, but one must remember that a player first tries to match a card in their hand with one on the table, then they take a card off the stack and try to match that, then they look to see if any Yaku were formed and decide what to do if they were.
Oh, and either player can go first so it needs to work regardless of who is first and who is second.

 

Whether you create the specification in your head, or write it down, it must be given due thought before a single line of code is written. You see, programming is a lot like baking. You need to have in mind your objective (Victoria Sponge), and then you have to know what is necessary (self-raising flour, sugar, eggs...) and the steps required in order to make it happen.
You can't just dump some random stuff into a bowl and hope for the best. Same with programming, you can't open an editor window and bang out some code without objectives in mind.

 

The next step is to flesh this out more. For example, think about what sort of data structures will be required to hold the various states, and about how the thing will look, feel, and behave. The card array was specified, and here is an excerpt of the outline of how the workarea was going to "be":

Window dimensions
=================

The main window size is 1780 x 980 OS units. It is set to require
application redraws, and to have a "blank" background. During the redraw
loop, the application will rectangle-fill the background exposed during
the redraw with the background green colour, and then the cards will be
drawn on top (possibly over existing cards). This minimises screen clearing
and should likewise minimise flicker. The redraw region is examined, and
only the zone(s) that need to be redrawn are redrawn, to further speed up
redrawing.

The window has scroll bars; both to allow it to be resized to suit the
user, and to permit the game to be playable on smaller (i.e. Beagle/RPi
analogue video) displays - for the window in pixels is 911 x 530 (including
furniture).


Card size
=========

The cards are represented with three different sizes:

  CARD_WID (62<<1) and CARD_HEI (106<<1) is the physical size of the card
  sprite.
  
  CARD_GRW (67<<1) and CARD_GRH (111<<1) is the size of the card with a
  small (approx 2px) black border. To keep eventual filesize of the card
  sprite data down, the black borders are drawn, not included as part of
  the sprite.
  
  The relationship between the X,Y position of the top left of the card
  and the position used by the sprite plot is X+6, Y+4. These offsets are
  defined as SPRITE_PUSHX (6) and SPRITE_PUSHY (4).
  
  The halfsize cards in the captured card areas are represented using
  similar constants - HCRD_WID (62), HCDR_HEI (106), HCRD_GRW (67) and
  HCRD_GRH (111). There are no half-sized sprites. The operating system
  plots the full size sprites scaled to 50% on the fly.
  The relationship between the X,Y position of the top left of the half
  size card and the sprite plot is X+4,Y+2; SPRITE_HPUSHX (4) and
  SPRITE_HPUSHY (2) provide these offsets.
  
  The two-thirds cards when a card has been "captured" and is shown as
  such on the table uses TCRD_OFF (22<<1) as an offset from the left side
  of the parent (full size) card. The card itself is then defined using
  TCRD_GRW (44<<1) and TCRD_GRH (74<<1) to supply its full size. The
  sprite is plotted using the same offsets as the parent card, only
  shifted to the left as defined by TCRD_OFF. Thus, the sprite dimensions
  do not need to be known, as the plotting already works as expected.



Zone definitions
================

The six zones, plus an "invalid" marker, are defined (in h.redraw) by:
  ZONE_INVAL (0) for an invalid zone
  ZONE_TABLE (1) for the table zone
  ZONE_STACK (2) for the card stack
  ZONE_PHAND (3) for the player's hand
  ZONE_OHAND (4) for the opponent's hand
  ZONE_PCAPT (5) for the player's captured cards
  ZONE_OCAPT (6) for the opponent's captured cards

Zones should be referred by definition, not magic values.


Zone 1 - Cards on the table
===========================

The cards on the table are arranged as two rows of seven cards. During game
play, it is usual to have two rows of four cards - and this is the layout
that Koi-Koi will favour. The additional three places in each row is for
"overflow" if there are more than eight cards on the table.
The game will stop with an error message upon a condition of more than 14
cards on the table at any given time. In numerous games with my mother, the
highest *real* number of cards in play at once was 12. I believe having >14
to likely be a deliberate action.

The width and height of the table are defined by TABLE_WIDTH (7) and
TABLE_HEIGHT (2).

The X,Y offsets of the top left of th UPPER row of cards is defined by the
values TABLE_X (86<<1) and TABLE_Y (134<<1). The horizontal spacing between
the cards is defined by LIST_SPC (70<<1).

Everything in CAPITALS refers to a definition in the "koikoi" header file. The C pre-processor will spot these in the code and replace them with the appropriate values prior to compilation (Google for "C preprocessor" if you don't know how this works).

 

The next thing I did was to design a set of templates (window definitions), create some generic Wimp code to get a basic program shell running, and then some Hanafuda cards. The cards were, originally, simply scans of a real Nintendo Hanafuda deck.

Following that, I wrote all of the code to draw the table, break it into zones, and to respond with a zone and card location based upon clicks in the window. There were no cards, only crossed boxes. You can actually see remnants of this if you go to the Settings window, expand it to full size, and select the "Show placeholders" option. A lot of work went into making the code that means I can click a place in the window, and the game will know that I just clicked the third card on the upper line of the cards "on the table".

It was necessary to write that harness and test it fully before anything else could be done.

So, fast forward six years (eek!) and I had a harness that displayed a layout suitable for a game of Koi-Koi. The next stage was to load and define the cards, deal them randomly, and have the game populate the work area according to what was happening. The game behaviour grew organically, aided by the fact that I played numerous games with my cat in order to get the game behaviour back into my mind. At first, the human player always went first and the computer player just chose cards semi-randomly by "what had the highest value". It stayed like this while numerous parts of the game behaviour were refined. Then arbitrator was modified so that either player could go first. And the rest of the changes (smarter computer player, yaku detection, scoring...) were eventually added.

Each step building upon the one before...
No point looking at gameplay until the work area works.
No point looking at the computer player until we've dealt with the human player.
No point looking at who-goes-first until the human-goes-first behaviour works.
No point looking at Yaku detection until gameplay happens correctly.
No point looking at scoring until Yaku detection works.
...and so on.

 

I'm not going to discuss algorithms. There are literally hundreds. Here is an example of the computer player's basic (simple level) selection of which cards to play:

   // Does something match this card?
   if ( cardhandler_whatmatches( cardid ) == 0 )
      return FALSE;

   // Scan the table looking for what matches
   for (recurse = TABLE_FIRST; recurse <= TABLE_LAST; recurse++)
   {
      if ( card[ table.table[recurse] ].state.is_matching )
      {
         // Note that the difficulty settings below are GREATER THAN
         // OR EQUAL TO in order to reduce code duplication.

         if ( config.difficulty >= 1 )
         {
            // DIFFICULTY : SIMPLE
            //
            // Simply get the points for the two cards
            ourcardpoints = card[cardid].points;
            tablecardpoints = card[ table.table[recurse] ].points;
         }
         
         
         // OTHER DIFFICULTY LEVELS (CODE HERE) BUILD UPON THIS.
         
         
         // Now add them together to see if this is our most valuable match
         thisscore = ourcardpoints + tablecardpoints;

         // Best scoring combination so far?
         if ( thisscore > highscore )
         {
            // Yes!
            highscore = thisscore;
            highcard = table.table[recurse];
         }
      }
   }

   // There will be a valid score and card, as something matches (already determined that)
   best.score = highscore;
   best.card = highcard;

   return TRUE;
}
This is called for a given card to match it against every card on the table to track the highest scoring match (highscore) and when all cards on the table have been examined, to assign the value to best.score. This, as it is, works for individual cards.
For picking a card from the computer player's hand, we simply call this function for each card in turn, and track which best.score (and associated best.card) gives us the highest value pairing.

The above function forms the basis of the computer player's intelligence. It represents the "simple" level of gameplay. Do you see where it says about other difficulty levels building upon this? Well, how the intermediate level works is to basically bias the card scores by applying a weighting. For example, all of the ribbons have the same points value, however poetry ribbons and the blue ones are weighted to be more valuable so if a match should be between a Plain and a Ribbon or a Plain and a Poetry Ribbon, it will choose the Poetry ribbon.
And, finally, the advanced level games the weightings depending upon what is in the capture areas. Yes, plural, it will look at what cards the human has captured, so if the human has two blue ribbons it'll put a weighting on getting the third (to block the human getting a Yaku for all three).

Many algorithms. :-)

 

Advice:

  • Your specification and array definitions are not set in stone (unless data is sourced from elsewhere). For instance, I had a data flag that identified a card as "Saké Cup, Full Moon, or Sakura curtain", but I needed to use constants to identify which is which. I didn't like that, so that flag was split into three so the three cards can be uniquely identified. In theory it should be possible to completely rearrange the Hanafuda card assignments (cards 1-48) - but I've not tried this (nor is it ever going to be a supported feature, just a potential side effect of the card structure defining itself and trying to reduce assumptions).
  • If you change an array in the code, update the spec/documentation. Docs that don't match the code are worse than useless, and a good programmer will have justification to trust neither.
  • Use comments and whitespace. Both are free in C code, use them.
  • Test and debug major functions as you write them. Write some harness code to feed your function various values (including bogus values) and record the result.
  • Seriously - test and debug as you go. It'll make things simpler in the long run, rather than having a situation where a big pile of untested code fails and you aren't sure why.
  • Leave all error and warning messages at their normal settings (usually ON) and aim to have every build be a clean compile - by that I mean no throwback window. No warnings, zero messages. Every time. Don't leave all those quirky things like "variable 'meh' declared but not used", or "case of 'int' to differing enum", or "use of '=' in condition context", or "lower precision in wider context: '/'", etc may be annoying but they're telling you that there's something wrong with your code.

And remember, this is not official programming advice. There are plenty of books, tutorials, teachers, and websites that will instruct you on the proper way to program. I'm writing this from the point of view of somebody who had an idea, worked out the steps necessary to make the idea become, then went and did it.

At any rate, if you've just opened your editor window with the idea of writing a program, then it's time to go put the kettle on. Before you write one single line of code, make a cup of tea and wander around. Walk around the house/block or up the road and back. What do you want? Prime Directive right there - know your end result. Then start thinking about how to make it happen.

 

 

Your comments:

Please note that while I check this page every so often, I am not able to control what users write; therefore I disclaim all liability for unpleasant and/or infringing and/or defamatory material. Undesired content will be removed as soon as it is noticed. By leaving a comment, you agree not to post material that is illegal or in bad taste, and you should be aware that the time and your IP address are both recorded, should it be necessary to find out who you are. Oh, and don't bother trying to inline HTML. I'm not that stupid! ☺ ADDING COMMENTS DOES NOT WORK IF READING TRANSLATED VERSIONS.
 
You can now follow comment additions with the comment RSS feed. This is distinct from the b.log RSS feed, so you can subscribe to one or both as you wish.

GAVIN WRAITH, 22nd December 2018, 13:54
+1. Happy Christmas.
Carl Johnson, 22nd December 2018, 23:32
Hi, sorry that this comment isn't related to your post, but I wanted to ask. Are you planning on doing any more stories with photos of playmobil figures? I thought those were pretty cool. 
 
By the way, merry christmas!
David Pilling, 23rd December 2018, 15:47
Forget regulating drones - you can buy the bits and make your own, and go back 60 years people were making model planes. I have a book showing the wonderful planes being made behind the iron curtain by individuals in the 60s. 
David Pilling, 24th December 2018, 16:00
How can they regulate drones that only exist in people's minds - no law can stop flights of fancy - seeing one's fears.
David Pilling, 24th December 2018, 16:02
"Why isn't Bob coding", if you were being paid by the line you'd not be wasting all this time. Man plans, God laughs.

Add a comment (v0.11) [help?] . . . try the comment feed!
Your name
Your email (optional)
Validation Are you real? Please type 89749 backwards.
Your comment
French flagSpanish flagJapanese flag
Calendar
«   December 2018   »
MonTueWedThuFriSatSun
     1
3456789
10111213141516
171819202223
2627282930
31      

Advent Calendar 2023
(YouTube playlist)

(Felicity? Marte? Find out!)

Last 5 entries

List all b.log entries

Return to the site index

Geekery

Search

Search Rick's b.log!

PS: Don't try to be clever.
It's a simple substring match.

Etc...

Last read at 00:58 on 2024/03/29.

QR code


Valid HTML 4.01 Transitional
Valid CSS
Valid RSS 2.0

 

© 2018 Rick Murray
This web page is licenced for your personal, private, non-commercial use only. No automated processing by advertising systems is permitted.
RIPA notice: No consent is given for interception of page transmission.

 

Have you noticed the watermarks on pictures?
Next entry - 2018/12/24
Return to top of page