(def! reduce (fn* (fn acc l) (if (empty? l) acc (reduce fn (fn acc (first l)) (rest l)))))

(def! join (fn* (l) (reduce str "" l)))

(def! tag (fn* (name content) (join (list "<" name ">" (join content) "</" name ">"))))

(defmacro! mktags (fn* (& names) (map (fn* (name) `(def! ~(symbol name) (fn* (& content) (tag ~name content)))) names)))

(mktags "html" "head" "title" "body" "h1" "p" "ul" "li")

(print
  (html
    (head (title "Hello world!"))
    (body
      (h1 "Hello" "world!")
      (p "Writing from inside mal (lisp) seems to work, yeah?")
      (apply ul (map li '("one" "two" "three")))
    )
  )
)

Phew, that took a while!

As part of the whole "programmable site" thing, I've been wanting to write html-as-lisp (because it's fairly easy to type brackets and quotes on my phone keyboard, at least compared to other syntax).

I've been able to do the basic version for a while - use a macro to make functions that take their content, convert it to strings and wrap it in tags. The problem has been that when I generate content on the fly (e.g., the li tags above), the brackets from the list end up embeded in the outcome.

Until now! Because apply flattens it's arguments, I can use it to convert the list result of map into the arguments of ul.

Sweet. That should be a bit of incentive to get more work done on mal-sharp.


(def! tag (fn* (name content) (str "<" name ">" (apply str content) "</" name ">")))

(defmacro! mktags (fn* (& names) (map (fn* (name) `(def! ~(symbol name) (fn* (& content) (tag ~name content)))) names)))

(mktags "html" "head" "title" "body" "h1" "p" "ul" "li")

(print
(html
  (head (title "Hello world!"))
  (body
    (h1 "Hello" "world!")
    (p "Writing from inside mal (lisp) seems to work, yeah?")
    (apply ul (map li '("one" "two" "three")))
  )
))

Turns out that I don't need join or reduce, since apply str does the same thing. Neat.

Also, I'm still not sure what I'm going to do about attributes. I want to be able to do something like (html {"lang" "en-gb"} ...), with the hash argument being optional, but I need to work out how to do that. (and clearly, two versions of tag, and an if in the macro to pick the right one, but I'm still not wild about it).


The mal interface still isn't right (ignoring the bug that means it stops working when I tab away).

I've got a rough idea of what I want, which is clicking "Exec" takes a copy of the textarea and adds it to a history along with the result (either whatever result was printed, or the details of whatever error was thrown), and probably a "reload" button.

I'll mess about with it sooner or later.


Not much to report. A little toothache in lower left 6(ish), Husband has reorganised the cables/tidied the box room, and a new version of Hydra (the OAuth2 engine I'm using here and at $work) has been released.

Otherwise, one sleep, one day at work, and then a week to relax (ignoring birthday weekend/prep).




There's this thing where I feel crap because I haven't eaten (and it's happening enough that I can recognise it), and all I need to do to feel better is to eat, but sorting food out is just too much, yeah?

I can finesse it so far off there is sufficient snack food available (up to maybe peanut/honey sandwiches, but dealing with cheese is, again, too much), but I can't will myself to cook (which would properly deal with the issue for half a day or so).

It sucks! I know what the problem is, I know how to solve it, but I just... can't. (This might be the executive disfunction that I'm hearing about)


Snake priorities

  • Only add tail if not 100 health

Each direction is weighted by:

  • Possible head (high -)
  • Is food (High + if low on health, low - otherwise)
  • Towards food (low + if low on health, neutral otherwise)
  • Towards tail (high +)

Sort by weight and pick the best

int GetWeight(MoveFlags flags, bool hungry) {
  int weight = 0;
  if (Match(flags, MoveFlags.Head)) {
    weight -= 5;
  }
  if (Match(flags, MoveFlags.Food)) {
    weight += hungry ? 3 : -1;
  } else if (Match(flags, MoveFlags.FoodDirection) && hungry) {
    weight += 3;
  }
  if (Match(flags, MoveFlags.Tail)) {
    weight += 3
  } else if (Match(flags, MoveFlags.TailArea)) {
    weight += 2
  }
  return weight;
}

An important thing to notice is that I seem to have plenty of time (to do funky snake brain stuff). My end takes 2-3ms, plus what looks like about 15-20ms of round trip. Given than I've got 500ms, i can comfortably take 100-150ms to think.

What should I think about?



My snake has been winning matches! Still losing more than it's winning, but it's doing well for two days development!

Strategic next is prediction. My current biggest cause of death is head on collisions that could have been avoided if I'd had even one more turn lookahead, but I don't want to stop at one turn. I've got maybe 300ms to play with, that's plenty of time to explore a biggish tree.

Brute force suggests that each turn generates 4 (snakes) * 3 (possible moves that aren't instant suicide) = 12 possible next turn states. That goes up with the power of the number of turns, so 12, 144, 1728 for the next 1,2, or 3 turns.

I need to decide/work out what sort of data I want out of the algorithm. I don't want to run my full heuristics for each snake, because I can see that my design choices are different to other snakes.

As an early efficiency, I'm going to ignore any snake who's head is at least (max depth + 1) squares distant, as they shouldn't be able to cause trouble in the planning horizon.

Then, for each remaining snake, calculate the list of non-suicidal moves.

I'll add a little bit of weighting here, e.g., a positive weight for a winning head to head, a negative weight for a losing one, small positive for carrying on in the same direction.

Then resolve the 'best' move for each opponent, breaking ties towards messing with me.

Push that state onto a stack, and calculate my move for that new state.


I'm not convinced. I think what I want to end up with is the chance that there's going to be a snake in any given square, adjusted for how many turns of lookahead I've got time for. Then, my snake should pick a path with the smallest chance of encountering another snake, subject to the rest of the weighting.

If it's just me on one side of the board and opponent on the other, then it's a big sea of zero probabilities. If I'm right up close to someone else, then there's something like a 1/3 chance to encounter another snake (and I'd hope to not get into that position).

I'll have another think/play in the morning.


Idea: Functions have (or rather, need) two 'channels' of input/output. One for the parameters and result, and one for metadata like did the call succeed.

  • Java/C# call the second channel 'Exceptions'
  • Go returns an extra result.
  • C embeds it in the primary result, or uses global state ("errno") (or both).
  • Haskell/functional languages put it in the type system.

HTTP calls them 'headers' and has scope for a lot more data then just success/failure (and maybe failure reason), but that doesn't count because it's not a programming language.

I think Lisp (or rather, some dialects of Lisp) allow the programmer to add metadata to variables. With a few support functions, that could be used to tag values before they're returned (e.g. with success/fail, expiry time, confidence level).

Or maybe Haskell is right, and that stuff all belongs to the Type.


I have been very much enjoying my time off work. I've been developing my snake for BattleSnakes, with an uncertain outcome (my snake goes up and down the rankings like a pogo stick, but that might be because I don't give it enough time to settle before changing the algorithm).

More fillings yesterday, hoping that's the last set but who knows?

Birthday tomorrow, going to go out to the hills and say hello to some windmills, and then a stupid burger from Burger King.

A couple of old friends are due for the weekend, husband is 50 and a bunch of people will be over in Sunday (eeek!).

Today is meant to be tidy and last minute prep. We'll see how that goes, I'm off back to sleep.


Snake is doing better, fixed some silly bugs, now it's all about tweaking relative weights (is heading to the center of the board worth more/less/the same as eating? Does it make a difference of my health is high or low).

Ideally, I could have the weights as an array, or something, so I could swap them in and out at runtime. (Domain Specific Language you say?)

I could use Lox fairly easily, especially if I didn't bother implementing functions (although having said that, I can see uses). I wonder how the Dev team would react to something like a blockly system for people to write hosted snakes with?

(Or they could add Oauth so 3rd parties can run snakes for you...)

(I'm being stupid: User creates a snake, and gives it a generated URL from my site, the BattleSnake devs shouldn't care).


I'm still working out how to do look ahead (in snake). It's something like 'generate a (good) move for each snake, update the board, iterate', except I want a probability field that shows the likelyhood of finding a snake at a point after n turns.

I think it's the feedback that's causing me problems: I can't just calculate the snakes independently because they interact, and I've got to know where the other two snakes are (could be) to calculate the moves for any given snake.

Except each snake only has three possible moves, and they're all known (although the probability of each isn't). Also, generating those moves is cheap enough that I can do it three times.

Ok. That gives me:

  • For each snake, generate moves that don't take me out of bounds, or collide with another snake. These are the possible position of the head of each snake after one turn.
  • For each unique combination of taking one move from each snake, generate a board with the snakes having moved, and generate a new set of moves. These are the possible positions after the second turn.

I should be able to flatten that into a single, iterative algorithm:

  • Given a list of possible positions for each snake, generate a new board for all combinations of positions. Then, use each of those boards to generate a new list of potential next moves for each snake.

Which leaves me with the "generate combinations" problem.

I can see how to do it with nested loops, but I'm having trouble seeing how to dynamically create a set of nested loops.

Back to basics:

  • I start with a Dictionary<Snake, List>
  • I want List<(Snake, Point)>

Markdown display

State machine

Need to peek at next element and then run state change

Need a "Write stuff until type" method

Section type, has title and content, based on level 2 headers

States

  • Start
    • Read Header -> Start Section Write everything
  • Start Section Write start section stuff, then to Section
  • Section
    • Read Header -> End Section
    • EOF -> End Section
  • End Section
    • Close section, then to Start

Still not sure what to do about Fediverse/Masterdon. Choice is between installing a server or writing my own. Writing my own is a bunch of work, which also counts as fun, gives me control, all that jazz. Installing an existing server is (or should be) much less work, but I have to take it's opinion on how things work.

Also, I'm still not convinced that it's worth the effort. I didn't get that much traction on Twitter, and I'm fairly sure that most of the people I'd want to follow have moved, or are planning to.

And I have to remember that a server isn't a client, although I could probably combine the two.

I could write a feed reader (which has the advantage that I could casually subscribe to blogs), or I could write a feed-to-fed gateway (which probably isn't a bad place to start. Hmm)


I need to make some choices about data storage, and really I think I mean that I've got to accept Entity Framework into my life.

Maybe the switch from Postgres to SQLite was a mistake. Yes, SQLite has a much lower footprint, and is easier to recover data from (theoretically, at least), but the EF migration stuff is pretty neat, and Postgres has a bunch of neat toys (and I should sort out some kind of useful "backup to text" system anyway).

wepiu has plenty of resources, and it's easy enough to script "create a db/create a user". Maybe I should swap back.

Before I do, I want a sensible set of rules so that different apps don't step on each others toes. Posibly including separating out auth from webmail. Hmm. Again.


The actual physics of FTL are way beyond me, but I'm happy to share a users understanding.

Imagine another universe (or dimension, this explanation is wrong enough that it doesn't make a difference) where every point in this universe maps exactly to a point in the other universe (usually called otherspace unless you've trying to get a paper published in a really posh journal). That's useful because, although there's a one-to-one mapping between points here and points there, points that are next to each other here aren't next to each other there, and can be very far apart.

This is very useful for travel if:

  1. You can get from here to otherspace
  2. You can find a point in otherspace that maps to your destination (in, say, the next star system)
  3. You can leave otherspace at the right point

A common theme of documentaries about the history of FTL is the great cost of time, effort, resources, lives, and rehabilitation of early travellers of solving the third of those problems, but these days even small ships can be fitted with enough of an FTL system to bounce from one well travelled system to the next.

As a more independent minded ship, my FTL systems are more complicated and better speced, but they operate much the same way:

  1. Use the pulsar timing network to get a good position fix. (This is a normal background task anyway, but it's worth checking that the results make sense. I bent an antenna once and thought I was about to crash into a moon. That wasn't a great day)
  2. Talk to the system location beacon to get updates about otherspace drift (did I mention that the points in otherspace move?)
  3. Run route calculation based on the first two steps
  4. Manouver very carefully to match the beacon's instructions.
  5. Pour a bunch of energy into the holepunch (Yes, it's got a proper technical name, no, I'm not going to bother dredging it out of cold storage)
  6. Flip the switch (metaphorically) and emerge into otherspace
  7. Scan for, lock on to, and move towards the signal from the destination beacon. Depending on your start and end points, you might need to route through a few different beacons.
  8. Arrive near your target beacon and again manouver to sit in the right place relative to the beacon.
  9. Dump another pile of energy into the holepunch and flip the switch
  10. Move away from the ingress point before another ship tries to punch out into the same space This one is paranoia and training, it's something that's drilled into ships (and pilots, I guess), but even at busy beacons there hasn't been a collision in living memory.
  11. Wait for enough data from the pulsar timing network to accumulate to confirm you're in the right place
  12. (Optional) Inform your crew/passengers/cargo that transfer was successful and they can resume duty/leave their seats/prepare for unloading

System to system travel time is dominated by travel time to/from beacons in realspace, and then by inter-beacon travel in otherspace. The flip from one to the other is effectively instantaneous.


TODO - Saturday 2022-11-19

Hello people, it's a dull Saturday in mid November, going back to bed and staying there is high on the wish list, however:

  • Shopping

    I need food (well, yoghurt, popcorn, peanut butter, sandwich meat, iced gems, blueribbon bars)

    Husband needs a compression sock and ibuprofen cream (twisted ankle)

  • Snake

    I can add a public static GameState Move(this GameState game, IEnumerable<(Snake, Heading)>) method that uses the battlesnake rules to create a new board with updated positions for each snake. I can use my existing MoveEngine stuff to predict moves for snakes, and then start running predicton games.

  • Endless Sky

    New game I've picked up, at the very simple end of the spectrum that's got Eve/Elite at the other end. Fun, ish, but I keep dying of jumping into a system I didn't know was pirate infested, which is less fun.

  • Peter Grant

    Enjoying my second read through of the series, got to work out which book is next (and if I've still got any oustanding deliveries)


I really want to write a browser. The guide at browser.engineering looks like a good place to start, but I'm stuck at the first step: Picking a language.

  • C is ubiquitous, but it's GUI support is a bit pants, and I'd need to look into building for Windows (plus, it's C, so hard)

  • C# it's easy and has good support, and has a good GUI framework, assuming I want to write a WinForms app. Also missing easy JavaScript support.

  • I can't believe that I'm seriously thinking this, but I could use Lox/Mal/QuickJS. Write a thin wrapper around one of the C GUI toolkits and write as much of the actual browser in the higher level language as possible.

    It would scratch the "write it in C" itch, and probably make it easier to abstract out the GUI (and network stuff, and other low level bits).

    I'd be constantly arguing with myself about if something should be written in C or the high level language, except I could be all "write just enough in C to expose the interface and write the rest in HLL". (I'm mostly thinking Lox here, if I can a) finish a C implementation, b) get arrays working and c) get useful "everything is an object"-ness working)

    Parsing needs better string support (but really, strings are just arrays, mostly). I probably want something like threads.

    Ye Gods, I'm going to end up embedding QuickJS into Lox.

Why do I want to write a browser

Because it's there? Because a good chunk of my time is spent online and I don't like being dependent on other people.

Because I want better control. I want to see which CA a site is using, and pick and choose which CAs to trust. I want to turn off (and on) images, css, scripts, iframes easily, per page or per site.

I want to see what redirects I'm going through, what cookies are being set, what headers I'm sending and receiving.

Because it's my computer, damnit, and I'm tried of other people thinking they can control it just because I've downloaded a HTML document from their server.

Plan

While I'm tempted to build a scratch version in C#, I don't think it's going to teach me enough to be worth it. If I can come up with an easy way to run JavaScript then maybe, but without that there's no point.

Therefore, I need to evaluate C GUI toolkits, and sort out a Win64 C build environment.

(This project would be so much easier if either C# did JavaScript, or my desktop could do Linux GUI. Maybe I should just pay for that Windows X server)


Okidoki, WSL now supports Linux GUI apps out of the box. That makes life easier. I guess I'm going to do this in C. Gulp.


I'm sure I'm missing something obvious, so it's duck time.

If I'm waiting for input from a bunch of streams (in C), I'll have a select (or poll) statement that waits until one of the streams has data ready. Easy so far.

When data comes in on a stream, I want to feed it into a parser that does a bit of work and returns when it gets to the end of the data (or, more accurately, gets to a point near the end of the data where it can't parse the next token because it's not complete).

How do I store enough state so that the parser can continue where it left off last time?

I can't/don't want to 'just' cache the stream until it's complete because I don't know how big the stream is (and I'd rather parse as I go).

On the other hand, memory isn't that expensive, and I can afford to buffer tens of megs if needed. If the parser knows that it's not going to be called half way through a token, then it can keep state much more cleanly.

I was playing with "state machine as a bunch of functions" a while back, and I think this is probably a reasonable use case.

(I'm going to save the entry here and maybe carry on later)


Remember the context here. This is for the browser, not some universal solution. There's going to be the main page, which is probably HTML, and that's going to trigger CSS, JavaScript, and images.

HTTP responses can be comfortabley modelled as status line, headers, and body. Status line ends with \r\n. Headers end with \r\n\r\n, and don't need to be parsed until they've been received. They do need to be parsed before the body, to get either the length of the body, or that the body is chunked.

The spec says don't expect headers to take up too much space, and the status line should be tiny (relatively).

I've been thinking too much like a server - that I'm going to get random hits that I've got to handle right now. But I'm a client. I'm asking servers to send me data, I look at that data at my leasure, and then maybe I ask for more.


Also, remember to push as much up to Lox as possible. Handing Lox a Response object with a status code, hash table of headers, and a body, and then letting Lox do the parsing is a reasonable position to take.


Eeep, that was a long wait. However, hello my lovley (if imaginary) reader, I'm back.

Current Obssions

  • Don Coffey's cab ride videos. Don is a train driver who posts videos of the routes their train takes. The videos have ambient audio (meaning the recording of the inside of the cab), and often have commentry as subtitles. I'm finding them absolutley mesmirising. Nothing happens, for two to four hours! Except it does, because there's a lot going on (points, signals, bridges, tunnels, other trains) but there's no artificial drama (or real drama for that matter), and often really nice landscape.

    (As a side effect, I've rediscovered Raildar, which is a map of the UK rail network with realtime train information)

  • Also on YouTube, Blacktail Studio is a guy who makes really expensive tables (like, 15k$ tables) and films themself doing it, and then talks over the film. A bit more drama than Don, but not by much, and again, just relaxing noise.

  • I'm running through Crafting Interpreters again, this time with the goal of using lox as a language to build a browser in (per [browser.engineering)[https://browser.engineering/]).

    I feel this one needs a bit more justification than normal. (Or rather, I want to talk about my idea :) ).

    I don't like that Chrome has taken over the world. It was bad when Microsoft/IE did it, it's bad now that Google/Chrome are doing it, and it will be bad if/as/when the next people manage to kick Chrome off the top spot. I'm using Firefox as my daily browser, but I don't like relying on other people for things that I can do myself (see: webmail).

    I know that modern browsers are crazy complex, but a lot of that complexity isn't being used in my favour anyway. There are still plenty of simple sites with easy layout, not a lot of javascript, and intresting things to say.

    I like C# as a language/environment, but I don't like the way it's got Microsoft looming over it. I like C as an open, simple, ubiquitous langage, but it does get verbose. Using Lox as a starting point gives me (theoretically, at least) a high level language that I control.

Of course, it's not really that simple, but let's see how far I get, yeah?


Poot. Photo upload is broken again, which means I can't include the awesome 1970s map of proposed motorways round Newcastle. However, the pdf source has the original.



Whoooop!!

I've added arrays to Lox! I'm so chuffed! And the code is (fairly!) clean, so it feels right this time.

I added tokens for '[' and ']', and hooked in new array and index functions as prefix and infix operators on '['.

array uses (a slightly modified) getArgumentsto parse a list of , seperated expressions onto the stack, and then calls OP_ARRAY with the count/array length to ask the runtime to create an ObjArray.

ObjArrayis a tissue thin wrapper around ValueArray.

index compiles an expression and consumes the expected ']'. Then it calls either OP_GET_INDEX or OP_SET_INDEX depending on if we're in an assignment context or not..

I'm not done with arrays, I can only create fixed size arrays at compile time. I want dynamic arrays (at least push and pop), and to be able to create an empty (nil filled) array with the length known at runtime.

Still, a good start!


To remember your current position in the blog, this page must store some data in this browser.

Are you OK with that?