Subscribe: taw's blog
http://t-a-w.blogspot.com/feeds/posts/default
Added By: Feedage Forager Feedage Grade A rated
Language: English
Tags:
ast  bit  code  doesn  don  game  gem  hash  lot  make  map  might  much  new  people  ruby  star trek  test  time 
Rate this Feed
Rate this feedRate this feedRate this feedRate this feedRate this feed
Rate this feed 1 starRate this feed 2 starRate this feed 3 starRate this feed 4 starRate this feed 5 star

Comments (0)

Feed Details and Statistics Feed Statistics
Preview: taw's blog

taw's blog



The best kittens, technology, and video games blog in the world.



Updated: 2018-02-21T03:17:40.220+01:00

 



Review of Star Trek: The Orville

2018-02-17T23:55:54.918+01:00

It's usually not worth writing reviews of works with a lot of existing reviews. Either you agree with the consensus, and so provide very little information beyond what's already there, or you disagree, and the review probably says more about your taste than about the subject.Lack of consensusNot this time. There's no consensus at all. Critics absolutely hate The Orville:Rotten Tomatoes critics score - 19%Metacritic critics score - 36/100While the audience loves it:Rotten Tomatoes audience score - 93%Metacritic audience score - 8.2/10IMDb audience score - 7.9/10This is extremely unusual. Audiences and critics usually agree very closely.Meanwhile, Star Trek: Discovery has far higher critics score:Rotten Tomatoes critics score - 82%Metacritic critics score - 72/100and very mediocre audience score:Rotten Tomatoes audience score - 56%Metacritic audience score - 4.7/10IMDb audience score - 7.4/10So what's the deal here?It's simple, the critics are total idiots, and the audience is completely right. Star Trek: The Orville is awesome.The reviewThe Orville is 90% classic Star Trek and 10% Family Guy. This combination is just too sophisticated for the stupid critics, but it works really well.Star Trek was always a very optimistic and lighthearted world. Even its "dark" parts like DS9 would look like a comedy by standard of today's grimdark TV. Dark moments like In the Pale Moonlight were only so effective because they were used to sparingly against the backdrop of endless Ferengi get rich schemes, Odo and Quark's cat and mouse games, shenanigans by the station's kids, Kai Winn's pettiness, and so many other ways which made the future look so much brighter than world of today.And it couldn't have worked any other way. Star Trek has to be bright for moral dilemmas to be engaging. If the whole world is a grimdark shithole, moral issues become irrelevant, and only survival matters.The Orville continues doing what Star Trek was great at. Its universe, while technically not in Star Trek canon, is just like it - full of imperfect people still trying to be their best. They explore the universe, run into interesting aliens of the week, and seriously deal with new moral dilemmas just like in previous Star Trek series. It feels like it's actually doing a better job, with choices characters make having far more serious consequences, instead of being promptly forgotten by the next episode.Sure, previous Star Trek series didn't employ Family Guy style humor, but 80s'/90s' sensibilities wouldn't work on today's TV, and this change is far less drastic than turning Star Trek into another grimdark series like everything else.It's a must-watch for every Star Trek lover. It's still trying to perfect its formula, but it might very well end up as the best Star Trek series ever.The bonus review of Star Trek: DiscoveryTo be fair, I only watched the first episode, but that should be quite telling as I watched every other Star Trek series and movies, many multiple times.Discovery is simply not Star Trek. Maybe I'd have liked it if it didn't pretend to be one, but it's too late now.It's just ridiculously grimdark. The characters, the antagonists, the plot, everything on the screen is just ridiculously dark. It hurts to even look at the screen. There's not one scene in the whole episode where the lights are properly on - it's all dark greys and blacks everywhere.It's so ridiculously Not Star Trek that there's a damn mutiny in the first episode, by the first officer who wants to start a war with the "Klingons"!As if that's not enough, for some reason it ditches Star Trek races - Discovery "Klingons" look and act nothing like Star Trek Klingons, there's a few Vulcans in the background, but they decided to ignore whole Star Trek Vulcan canon anyway, and there's some humans and some weird new types nobody cares for.As far as I can tell - and maybe that changes later - it's doesn't even follow the crew ensemble formula, instead focusing hard on single character, who's a woman most annoyingly named Michael.So why does it even call itself Star Trek? That [...]



Let's Play XCOM: Enemy Unknown

2018-02-10T14:04:41.424+01:00

It's time for another classic game - XCOM: Enemy Unknown!

The Long War mod would make it last about 200-300 episodes instead of more reasonable number like 40 or so, and let's be honest - there's no way in hell I'd be able to finish it. Even people with a lot more time than me left a lot of Long War let's plays incomplete.

I got a bunch of minor mods, two most interesting are:
  • Tweaking perk tree, and perk pool for training roulette. This mostly fixes Snipers by making Squadsight the first perk, and delaying Headshot until major-tier perk. It also makes Supports into reliable medics by removing medic skills from the pool.
  • Making alien line of sight a bit shorter to occasionally enable ambushes. This could probably be cheesed by the kind of people who like counting tiles, but I never felt like doing so.
The rest is largely UI. Playing on Classic difficulty, so there should be some challenge, especially since I probably forgot everything about the game by now.

The let's play is almost exclusively battles, with base management in between cut out, as you probably don't want to see me alt-tab to spreadsheet and wiki and counting how much power I need to build when and how many corpses I need to sell on grey market to get necessary space bucks for foundry upgrades.

Here's the first episode:

allowfu320creen="" c266s="YOUTUBE-iframe-video" data-thumbnail-src="https://i9.ytimg.com/vi/sbqz7MPGpvY/default.jpg?sqp=CKTX-9MF&rs=AOn4CLBjgmJbHa_GoOUfjzT6abOqjTMKEg" frameborder="0" height="337" src="https://www.youtube.com/embed/sbqz7MPGpvY?feature=player_embedded" width="600">


As usual, episodes coming out once a day, same time of the day, until we win or aliens do.




London cycle hire adventures

2018-01-29T21:33:40.111+01:00

Today I felt like coming home with a cycle hire (commonly known as "Boris Bikes", officially now "Santanders Cycles" after a rebrand).

I went to a nearest dock, which the app claimed to be in perfectly fine order, and tried to get a bike. Error. Tried a few more times just in case, still error. I thought that's just app error, so I tried to use the terminal instead - which is really painful as it involves clicking same stupid confirmation dialogs about 25 times before it lets you rent the damn bike - but it was just as broken as the app.

Could the app somehow mark the dock as not working? Right...

So I walked to the next dock, that somehow wasn't broken, so I got a bike, cycled to Aldgate, and from there to the end of pompously named "Cycle Superhighway 2" at Stratford.

Stratford is a traffic hell, with no docks near the "Cycle Superhighway 2" or the station. The app claimed the nearest two docks had zero spaces left.

There was one about 10 minutes away from the station which claimed to have 4 free spaces. I went there - wasting tons of time going in circles because direct route is not possible anywhere near Stratford - and there was only one space, which didn't take the bike in spite of repeated attempts, for whichever reason. First it flashed red, then it just gave up.

After a lot of loud cursing, I had to cycle to the next dock, which fortunately somehow had free spaces, probably because it was really damn far from the station.

The cycling itself was fine, just everything else about the experience was totally miserable, and of course they charged me extra for wasting my time due to broken docking stations.

Which is pretty much what I learned to expect from the TfL. Unionized 💩💩💩.

I really hope some competition with dockless bikes comes to London. Right now none of these new schemes allow realistic commuting between Central London and zones 2/3, but there's some hope for the future.



Let's Play Civilization 5 as Rome

2018-01-23T22:36:56.977+01:00

It's been a long while since I last recorded any Let's Plays, so here's a new one!

It turns out not everybody switched to Civilization 6, and in fact new mods for Civilization 5 keep coming out. This campaign features one of them - 5th Unique Component. Other mods just as before.

As Rome, our unique abilities are:

  • 25% faster production in other cities of every building which is already built in our capital
  • Ballista - unique Catapult replacement - slightly stronger
  • Legion - unique Swordsman replacement - slightly stronger, can build roads and forts
  • Forum - unique Market replacement - +10% Great Person
  • Thermae - unique Garden replacement - +1 Science, +1 Culture, +1 Food.
  • Aedes - unique Temple replacement - +1 Global Happiness, cheaper to build, costs no maintenance, and doesn't require a Shrine.
Overall these feel like mid-tier or a bit below abilities. Faster production scales with number of cities, and Aedes gives us a bit of happiness breathing room, so together they support a bit of expansion. Unique units come too early and are of wrong kind to have much impact, early wars are dominated by Composite Bowmen supported by Horsemen.

I'd definitely love to hear feedback, especially technical feedback - I'm recording at higher resolution, and I'm not totally sure if I set OBS correctly for it.

allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i9.ytimg.com/vi/XfwfkIWUyyk/default.jpg?sqp=CPzGntMF&rs=AOn4CLAnq9-IZM2KUmwJihRJAPXehDQbGg" frameborder="0" height="360" src="https://www.youtube.com/embed/XfwfkIWUyyk?feature=player_embedded" width="480">

One video a day, full playlist going to be here.







New Hash methods in Ruby 2.5 and hash-polyfill

2018-01-20T20:41:14.911+01:00


Ruby 2.5 includes a bunch of new Hash methods:
  • Hash#slice
  • Hash#transform_keys
  • Hash#transform_keys!
The first two do exactly what you'd expect them - and the same ActiveSupport's methods with the same names do. In case of key conflict Hash#transform_keys will quietly overwrite keys, which is somewhat questionable behaviour, but it's not like there's obvious better way.

Unfortunately Hash#transform_keys! took some shortcuts, resulting in rather questionable behaviour. I submitted it as a bug report, and hope they fix it soon, but to be honest track record of my Open Source bug submissions is rather poor.

I'm really surprised Hash#compact wasn't included.

If you want to use these new methods in older Ruby versions, or if you want to use methods from future rubies like Hash#compact, Hash#select_values, Hash#select_keys etc., I updated hash-polyfill gem too.

I did not include Hash#transform_keys! in the gem as its unclear if it will have corrected or current questionable behaviour long term.



Challenges for November 2017 SecTalks London

2017-12-25T11:26:02.233+01:00

Following highly successful September round of London SecTalks, I ran another round in November.The round consisted of 8 tasks, and they were a bit harder this time, with even the winner only finishing 7 in time - a few people completing the challenges only after time.You can find challenges and code used to generate them in this spoiler-free repository.This post doesn't contain answers, but it might spoil a bit.Archive (5 points)It was nearly identical to previous round's archive challenge - 16-level nested archive, with 1 real and 15 fake archives on every level. The only difference was that distraction files were 0-padded to have same size as the real file, which forced smarter strategy than simply going for the largest file every level.Of course MD5ing to find unique file, or just unpacking them all and removing duplicate files still worked.CSS (10 points)The password was encoded within CSS rules. I've never seen this kind of challenge anywhere, so maybe it's the world's first?It was very short, and every character was independent, so it seems that everyone just manually brute forced it.Secret Message (15 points)The answer was written in one color on background of another extremely similar color. Everybody managed to finish it so quickly, I didn't even have a chance to see what kind of tools they used to solve it.EDIT: Oops, it seems that I messed up ImageMagick options and also accidentally left the answer in EXIF.Python (20 points)As we all know Python is a whitespace-sensitive language. So I encoded some secrets in the whitespace.Quite a few people used editors which cleaned up whitespace automatically, messing up with the file. Once a person figured out what the challenge is about, it wasn't usually too hard to solve it.Ruby (25 points)Obfuscated Ruby challenge was the hardest one of the round. It used two layers of Unicode obfuscation, first with emoji, and then with CJK characters. Other than using unusual characters, obfuscations applied weren't particularly hard.ECB BMP (30 points)This was a fun one. It was basically a version of the famous ECB penguin from Wikipedia.People had a lot of trouble figuring out dimensions and bit depth of the image, which had to be given as a hint, even thought they were fairly usual.XOR GIF (35 points)This was a two step challenge. A GIF file was xor-encrypted with a word from a dictionary.The challenge was then to find out which Twitter account the image is from.Since GIF header is known, it was very easy to figure out the first few letters of the key. However, people had a lot of trouble completing it, as the word I've chosen was only in some dictionaries. This wasn't intentional.After getting the image, it turns out only some reverse image search could find it properly, and others returned bogus matches.ROT Word (40 points)I wanted to have a task for statistical analysis of some classical cipher, but all the real ones have online tools you can use to solve them in a few seconds.So I made up one - it's like rot cipher with multi-letter key, except each letter is used for whole word, not for one letter.encrypt("All your base are belong to us!", "omg") == "ozz kagd hgyk ofs nqxazs zu ig"For a bit of extra challenge the message was in English, but contained a bunch of non-English proper names.Final ThoughtsI made this one just a bit harder, and maybe it was a tiny bit too much.Overall, a lot of fun happened.I'd definitely recommend CTFd server for this.[...]



How to watch high speed let's plays on London Underground

2017-11-28T20:07:26.554+01:00

Apparently the idea that some places - like London trains - are offline - never occurred to anyone in California or Seattle or wherever people who write mobile software tend to live. And even support for high speed playback is not quite as common as it should be.So I came up with a process to deal with it - which even with all the scripts to automate it still has far too many steps. I'm not saying I recommend it to anyone, but maybe someone needs to do something similar, and they might use it as a starting point.Download let's playsFirst, let's find a bunch of let's plays we want to watch, it's best to use playlists instead of individual videos to reduce URL copy and pasting time, but it works for both.To download them we can use youtube-dl, which is available as a homebrew package (brew install youtube-dl), or you can get it from here.$ youtube-dl -t -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" "url1" "url2" "url3"Youtube offers videos in many formats, and arguments above are what I found to result in highest quality and best compatibility with various video players. Default settings often ended up causing issues.Speed up the videosThere's plenty of command line tools to manipulate audio and video, and they tend to have ridiculously complicated interfaces.I wrote speedup_mp3 script (available in my unix-utilities repository) which wraps all such tools to provide easy speedup of various formats - including of video files.The script uses ffmpeg to speedup videos - as well as sox and id3v2 to deal with audio files if you need to do so to some podcasts. All those dependencies you can satisfy with brew install ffmpeg sox id3v2.The script can speedup a whole directory of downloaded videos at once by 2.0x factor:$ speedup_mp3 -2.0 downloaded_videos/ fast_videos/Adjust that number to your liking. Factors higher than 2.0 are not currently supported, as ffmpeg requires multiple speedup rounds in such case. I plan to add support for that later.The process takes a lot of time, so it's best left overnight to do its thing.The script already skips videos which exist in target directory, so you can add more videos to the source, run it again, and it won't redo videos it already converted.Put them on DropboxInfuriatingly there doesn't seem to be any good way to just send files to Android tablet from a laptop over WiFi. There used to be some programs, but they got turned into microtransaction nonsense.If you already use Dropbox, the easiest way is to put those sped up files there. This step is a bit awkward of course, as video files are big, people's upload speeds are often low, and free Dropbox plan is pretty small.If that doesn't discourage you, open Dropbox app on your tablet, and use checkbox to make your files available offline. You don't need to wait for it to finish syncing, Dropbox should keep updating files as they get uploaded.After that any video player works. I use VLC. Just open Dropbox folder, click on the video, it will open in VLC and play right away. First time you do it, make sure to set VLC as default app to avoid an extra dialog.Isn't this ridiculously overcomplicated?Yeah, it sort of is. Some parts of it will probably get better - for example speed controls on video/audio playback are getting more common, so you could skip that part (watching at 100% speed is of course totally silly). It still makes some sense to pre-speedup to save space and battery on the device, as faster files are proportionally smaller, but if you feel it's not worth the hassle, you can probably find a video player with appropriate functionality.TfL showed zero interest in fixing lack of connectivity on London underground, and mobile ecosystem assumes you're always online or everything breaks, so this part will probably be a major pain point for very long time.The part I find most embarrassing is lack of any builtin way to just send files over to a device. [...]



10 Unpopular Opinions

2017-11-18T06:45:12.053+01:00

I posted these on twitter a while back on Robin's request, but I wanted to elaborate a bit and give some context.The list avoids politics, anything politics-adjacent like economics, and is not about just preferences.If these turn out to be not controversial enough, I might post another list sometime in the future.Listening to audio or watching videos at 100% speed is a waste of lifePeople speak very slowly, and for a good reason. When you talk with another person you need to not just process what they said, you're also preparing your responses, considering their reaction to your responses, and so on. With more than two people involved, it gets even more complicated.None of this applies when you're just listening to something passively. Using audio speeds designed to leave you with enough brainpower to model your interlocutor when there's no interlocutor to model is just wasting it.It will probably take a while to get used to it, but just speed it up, 200% should be comfortable for almost every content - podcasts, audiobooks, let's plays, TV etc. At first I used slight speedups like 120%, but I kept increasing it.Side effect of this is that you might end up listening to music at higher speeds too (I end up using 140%), and people find this super weird.I recommend this Chrome extension to control exact playback speed. It works with all major video sites.I also wrote speedup_mp3 command line tool for podcasts and audiobooks, but nowadays most devices have builtin methods.Oh and back in analog days, speeding up audio messed up the pitch, so everything sounded funny. It's not true for modern methods.Any programmer who does not write code recreationally is invariably mediocre at bestThis comes up every now and then on sites like reddit and the masses of mediocre programmers are always like "oh it's totally fine to just code at work". It's not.Coding is unique in its ability to change the world - with even tiny amounts of effort you can affect reality. If someone never codes recreationally, this means one of:They're so content they never needed or wanted to create something that didn't exist beforeThey coded some stuff, but never bothered to Open Source itThey'd like to, but they're just not good enoughSo when you're hiring, all CVs without github link should go straight to the bin.Couldn't be bothered to Open Source used to be sort of excusable, but it's nowadays just so easy to push something to github, Signaling 101 strongly implies people without github account are just bad.And that applies to even junior / graduate roles. Even if you don't have anything amazingly useful to show yet, you can still share as you learn.Avoidance of suffering can't be basis of morality - if it was, knocking out a few pain genes would be highest moral imperativeNobody buys morality systems based on "God said so" or "Kant said so", and when people spend too much time on utilitarianism, they run into all kinds of problems.So it became fashionable to ignore all pleasurable parts of utilitarianism, and just focus on minimizing suffering.This is a total nonstarter. "Pain" and "suffering" are not exactly the same thing, but if you want to minimize suffering getting rid of pain is pretty much mandatory, and it's just a few simple gene edits to abolish it completely.So far nobody's interested in researching some gene edits for humans or animals to get rid of pain, so by revealed preference they don't actually buy their own stated believes that avoidance of suffering is terribly important.An obvious objection might be that people with congenital insensitivity to pain keep getting themselves in physically dangerous situations, but it's completely irrelevant. They live in the world of pain-sensitive people, which is currently full of objects dangerous to people without pain sensitivity. It would take very modest effort to redesign common risk factors for greater safet[...]



Architecture of z3 gem

2017-11-01T04:48:37.420+01:00

This post is meant for people who want to dig deep into Z3 gem, or who want to learn from example how to interface with another complex C library. Regular users of Z3 are better off checking out some tutorials I wrote.Architecture of z3 gem The z3 theorem prover is a C library with quite complex API, and z3 gem needs to take a lot of steps to provide good ruby experience with it.Z3 C API OverviewThe API looks conventional at first - a bunch of black box data types like Z3_context Z3_ast (Abstract Syntax Tree), and a bunch of functions to operate on them. For example to create a node representing equality of two nodes, you call:Z3_ast Z3_API Z3_mk_eq(Z3_context c, Z3_ast l, Z3_ast r);A huge problem is that so many of those calls claim to accept Z3_ast, but it needs to be particular kind of Z3_ast, otherwise you get a segfault. It's not even static limitation - l and r can be anything, but they must represent the same type. So any kind of thin wrapper is out of the question. Very Low Level APIThe gem uses ffi to setup Z3::VeryLowLevel with direct C calls. For example the aforementioned function is attached like this: attach_function :Z3_mk_eq, [:ctx_pointer, :ast_pointer, :ast_pointer], :ast_pointerThere's 618 API calls, so it would be tedious to do it manually, so instead a tiny subproject lives in api and generates most of it with some regular expressions. A list of C API calls is extracted from Z3 documentation into api/definitions.h. They look like this: def_API('Z3_mk_eq', AST, (_in(CONTEXT), _in(AST), _in(AST)))Then api/gen_api script translates it into proper ruby code. It might seem like it could be handled by ffi library, but there are too many Z3-specific hacks needed. A small number of function calls can't be handled automatically, so they're written manually.For example Z3_mk_add function creates a node representing addition of any number of nodes, and has signature of: attach_function :Z3_mk_add, [:ctx_pointer, :int, :pointer], :ast_pointerLow Level APIThere's one intermediate level between raw C calls and ruby code. Z3::LowLevel is also mostly generated by api/gen_api. Here's an example of automatically generated code: def mk_eq(ast1, ast2) #=> :ast_pointer VeryLowLevel.Z3_mk_eq(_ctx_pointer, ast1._ast, ast2._ast)endAnd this one is written manually, with proper helpers:def mk_and(asts) #=> :ast_pointer VeryLowLevel.Z3_mk_and(_ctx_pointer, asts.size, asts_vector(asts))endA few things are happening here:Z3 API requires Z3_context pointer for almost all of its calls - we automatically provide it with singleton _ctx_pointer.We get ruby objects, and extract C pointers from them.We return C pointers FFI::Pointer and leave responsibility for wrapping them into ruby objects to the caller, as we actually don't have enough information here to do so.Another thing Z3::LowLevel API does is setting up error callback, to convert Z3 errors into Ruby exceptions.Ruby objectsAnd finally we get to ruby objects like Z3::AST, which is a wrapper for FFI::Pointer representing Z3_ast. Other Z3 C data types get similar treatment. module Z3 class AST attr_reader :_ast def initialize(_ast) raise Z3::Exception, "AST expected, got #{_ast.class}" unless _ast.is_a?(FFI::Pointer) @_ast = _ast end # ... private_class_method :new endendFirst weird thing is this Python-style pseudo-private ._ast. This really shouldn't ever be accessed by user of the gem, but it needs to be accessed by Z3::LowLevel a lot. Ruby doesn't have any concept of C++ style "friend" classes. I've chosen Python pseudo-private convention as opposed to a lot of .instance_eval or similar.Another weird thing is that Z3::AST class prevents object creation - only its subclasses representing nodes of specific type can be instantiated.SortsZ3 ASTs represent multiple things, mostly sorts and expressions. Z3 automatically[...]



How to properly setup RSpec

2017-11-01T02:48:37.489+01:00

This post is recommended for everyone from total beginners to people who literally created RSpec.Starting a new projectWhen you start a new ruby project, it's common to begin with:$ git init$ rspec --initto create a repository and some sensible TDD structure in it.Or for rails projects:$ rails new my-app -T$ cd my-appThen edit Gemfile adding rspec-rails to the right group:group :development, :test do  gem "rspec-rails"endAnd:$ bundle install$ bundle exec rails g rspec:installI feel all those Rails steps really ought to be folded into a single operation. There's no reason why rails new can't take options for a bunch of popular packages like rspec, and there's no reason why we can't have some kind of bundle add-development-dependency rspec-rails to manage simple Gemfile automatically (like npm already does).But this post is not about any of that.What test frameworks are forSo why do we even use test frameworks really, instead of using plain ruby? A minimal test suite is just a collection of test cases - which can be simple methods, or functions, or code blocks, or whatever works.The most important thing test framework provides is a test runner, which runs each test case, gathers results, and reports them. What could be possible results of a test case?Test case could passTest case could have test assertion which failsTest case could crash with an errorAnd here's where everything went wrong. For silly historical reasons test frameworks decided to treat test assertion failure as if it was test crashing with an error. This is just insane.Here's a tiny toy test, it's quite compact, and reads perfectly fine:it "Simple names are treated as first/last" do  user = NameParser.parse("Mike Pence")  expect(user.first_name).to eq("Mike")  expect(user.middle_name).to eq(nil)  expect(user.last_name).to eq("Pence")endIf assertion failures are treated as failures, and first name assertion fails, then we still have no idea what the code actually returned, and at this point developer will typically run binding.pry or equivalent just to mindlessly copy and paste checks which are already in the spec!We want the test case to keep going, and then all assertion failures to be reported afterwards!Common workaroundsThere's a long list of workarounds. Some people go as far as recommending "one assertion per test" which is an absolutely awful idea which would result in enormous amounts of boilerplate and hard to read disconnected code. Very few real world projects follow this:describe "Simple names are treated as first/last" do  let(:user) { NameParser.parse("Mike Pence") }  it do    expect(user.first_name).to eq("Mike")  end  it do    expect(user.middle_name).to eq(nil)  end  it do    expect(user.last_name).to eq("Pence")  endendRSpec has some shortcuts for writing this kind of one assertion tests, but the whole idea is just misguided, and very often it's really difficult to twist test case into a sets of reasonable "one assertion per test" cases, even disregarding code bloat, readability, and performance impact.Another idea is to collect all tests into one. As vast majority of assertions are simple equality checks, this usually sort of works:it "Simple names are treated as first/last" do  user = NameParser.parse("Mike Pence")  expect([user.first_name, user.middle_name, user.last_name])    .to eq(["Mike", nil, "Pence])endNot exactly amazing code, but at least it's compact.Actually...What if test framework was smart enough to keep going after assertion failure? Turns out RSpec can do just that, but you need to explicitly tell it to be sane, by putting this in your spec/spec_helper.rb:RSpec.configure do |config|  config.define_derived_metadata do |meta|    meta[:aggregate_f[...]



Challenges for September 2017 SecTalks London

2017-10-21T15:39:57.255+02:00

SecTalks's name suggests that it might have started as "security talks" meetup, but its format evolved and it's primarily about doing security-related challenges.Usually the person who won previously, or another volunteer, prepares a challenge, and then during the meetup everyone tries to solve it. Whoever finishes first then gets to run the next one.The challenge usually takes multiple steps - once you solve one, you unlock the next layer.After my most recent victory I decided to tweak the format a bit. Participants vary a lot in level of experience, so with layered challenge it's common for many to just get stuck and not do much for the rest of the meetup.So instead I setup a CTFd server with 8 challenges which can be done in any order. This way if someone is stuck, they can just try another challenge.You can get the challenges and the source code I used to generate them from this spoiler-free repository.This post doesn't contain answers, but it might spoil quick a bit.Archive (5 points)It was a small bonus challenge, which I expected to be the easiest of all, but it somehow turned out to be hardest, with even the eventual winner solving it only after finishing everything else.The archive contained file with the answer and some distraction files. It was packed into another archive together with some distraction archive files. And so on a few more levels.It could be done manually, as distraction files were always identical, so you just go for the different one each time. Or it could be done with some simple Unix scripting.I'm not even sure why people had so much trouble with it, maybe they expected something more devious?Javascript 1 (10 points)Most of challenges were about reverse engineering password validation script. Finding your way in a messy Javascript is one of core skills nowadays.I wrote simple script with lines like these:checksum += Math.pow(password.charCodeAt(0) - 115, 2)And passed it through this obfuscator.Javascript 2 (15 points)Second Javascript reverse engineering challenge had more complicated validator.Every character went through a few variables where it'd get shifted by a constant. Order was reshuffled and then obfuscator would rename those variables, so it was a small puzzle, but it wasn't too hard to just walk backwards.var a5 = password.charCodeAt(5)var b5 = a5 + 80var c5 = b5 - 194I passed this script through this more complex obfuscator , with settings of string array encoding: Base64, string array threshold: 1.It was a lot harder than the first Javascript challenge.One Letter (20 points)This challenge is a cryptography classic - a block of text encoded by a monoalphabetic substitution cipher with spaces and punctuation removed.In principle it's totally doable manually in a few minutes, but people who tried to do it this way. Most solved it with online statistical analysis tools.Python (25 points)Finding obfuscated code in non-JS languages is rare, but everybody should have basic familiarity with wide range of popular languages.Validation script contained tests like these:    if ord(key[7]) + 97 != 197:        return FalseThen I used online obfuscator, but it's no longer available. I think it was based on this.There was extra step here, as it only worked in Python 2, and many people obviously tried to run it on the most recent version.Ruby (30 points)I couldn't find any ruby obfuscators, so I found some old one for 1.8, and based on it wrote my own.People had little trouble getting through the obfuscator, but the next step of dealing with validation script itself turned out to be really difficult.Validator concatenates password 4 times, then modifies it by taking out letters one at a time.  return unless password.slice!(10, 1).ord == 110Some people wrote scripts to reverse slice! into[...]



11 Small Improvements For Ruby - followup

2017-06-30T07:13:04.838+02:00

Last week I posted a list of 11 things I'd like to see changed about Ruby.Time for some followup, as I have workarounds for at least some of the issues.Pathname#glob - now in a gemI wrote a gem pathname-glob which provides the missing method so you can code:Pathname("foo").glob("*.txt")This doesn't just save you a few characters, it's the only way to get reliable globbing when the path contains special characters like [, ], {, }, ?, *, or \ - which is pretty likely if you're dealing with users' files.Support for special characters can't be fixed in Dir.glob("foo/*.txt") style API, as it doesn't know where name of the directory ends and globbing pattern starts.Another workaround would be to use Dir.chdir("foo"){ Dir.glob("*.txt") } - that'd deal with special characters in folder names, but would cause issues with threads.Of all the issues, this one is probably most useful to get into ruby standard library. It's like jQuery for Pathnames.Hash#zip - now in a gemI wrote a gem hash-zip.You can use it for zipping any number of Hashes together, and it will pad any missing values with nils. Usually the next step is to merge them in some meaningful way (for simple case when one overrides the other Hash#merge already exists).default_options.zip(user_options).map{|key, (default_value, user_value)| ... }Technically it overwrites existing #zip method Hash inherits from Enumerable, but Enumerable#zip really doesn't make any sense when applied to Hashes, so it's better this way than introducing a new name.Missing Hash methods - now in a gemI wrote a gem hash-polyfill, which contains a bunch of simple methods Hash is bound to get eventually.These are:Hash#compactHash#compact!Hash#select_valuesHash#select_values!Hash#reject_valuesHash#reject_values!Hash#transform_valuesHash#transform_values!As I assume these will be added eventually, gem only adds methods which don't exist yet. Gem skips already defined methods.In particular the last two already exist in 2.4, but you can get access to them from older Ruby versions with this polyfill.Naming convention for #compact, #compact! follows Array, and for #select_values etc. follows 2.4's #transform_values and existing Enumerable#select/reject.Names Ruby will end up using might be different, but these are as good guesses as any.Enumerable#count_by - easier with 2.4The example I had last time:posts.count_by(&:author)is actually not too bad with 2.4:posts.group_by(&:author).transform_values(&:size)For very large collections (like all bytes in some huge file), group_by / transform_values is going to take a lot more memory than counting things directly, but I ran benchmarks and it seems it's usually a lot faster than Ruby-based loop implementation.If you're not on 2.4, check out hash-polyfill gem.[...]



Simple Terrain Mapmode Mod for Hearts of Iron 4

2017-06-27T14:25:05.538+02:00

Paradox grand strategy games don't let mods create new map modes without some serious hacks - even something as important as truce map modes for CK2 and EU4 is still missing.Fortunately at least here a hack worked.If you just want to skip the explanations and get the mod:Vanilla versionKaiserreich versionHow Hearts of Iron 4 map worksIn most strategy games maps are generated dynamically, so there's always simple mapping from map data to what's displayed.Even a lot of games with static map use similar technique - gameplay map drives what's displayed.That's not how Hearts of Iron 4 and other Paradox games work. Instead, they contain a bunch of bitmaps driving what's displayed, and almost completely separate gameplay map data.So it's possible for a tile to look like a forest visually while being a gameplay hill. In some parts of the map like Persia or Tibet majority of tiles are not what they look like.This separation is supposed to make things look nicer, but I'd say it's not worth it. When players have a choice - like in Europa Universalis 4 - vast majority instantly disable default map mode (terrain) and nevel look back again; instead using political map mode 99% of time, and if they want to see terrain information they use simple terrain map mode which is driven by gameplay data.Unfortunately Hearts of Iron 4 doesn't let us disable visual terrain view. And to make matters worse it contains no way to see gameplay terrain. Instead we're forced to see clutter which might or might not match gameplay terrain in every map mode.Since visual and gameplay terrain match poorly, the result is that figuring out which way you can send your tanks is a hellish exercise of mouse-overing every tile to see its real terrain, and then trying to remember it all.What the mod doesThe mod changes visual map to make it match game map more accurately, by running some image editing scripts.It changes terrain and tree layers. It make everything far more accurate, and while it loses some detail, I'd say map looks cleaner and better this way.I experimented with changing heightmap as well, but that made the map look rather ugly, and other changes are usually sufficient to make terrain clear. Taiwan is an example of region where terrain and heightmap mismatch.It doesn't completely wipe out detail on source map, just tweaking it in places where there's significant mismatch.There are a few mods that try to make terrain look better by replacing textures used for various terrain - but they don't deal with core issue that data they're trying to display doesn't match in game map.CompatibilityThere are two versions:one for vanilla mapone for Kaiserreich mapYou can use them with other mods as long as they don't change the map - in particular Road to 56 mod works just fine, since it uses vanilla map.As it's all a script if there's demand I can run it for other mods. Or you could use it anyway, it might be better than vanilla map anyway.You can use it with mods that change map graphics.BeforeGood luck guessing where the mountains are. Or hills. Or deserts.AfterAnd these are the answers.IssuesSome minor rivers flow through a tile, and it's still some guesswork if you get river penalty or not attacking it. Mod doesn't affect river placement in any way.A number of tiles in vanilla are declared as "urban", even though game files declare them as something else. The mod believes game files. If anybody has a clue what's going on, I'd like to know. The only tiles affected that I noticed are a few VPs in Iraq region. Kaiserreich doesn't seem to be affected by this problem.[...]



11 Small Improvements For Ruby

2017-07-02T14:04:25.761+02:00

For followup post with some solutions, check this.A while ago I wrote a list of big ideas for Ruby. But there's also a lot of small things it could do.Kill 0 octal prefixI'm limiting this list to backwards compatible changes, but this is one exception. It technically breaks backwards compatibility, but in reality it's far more likely to quietly fix bugs than to introduce them.Here's a quick quiz. What does this code print: p 0123p "0123".to_ip Integer("0123")Now check in actual ruby and you'll see my point.If your answer wasn't what you expected, then it proves my point. The whole damn thing is far more likely to be an accident than intentional behaviour - especially in user input.If you actually want octal - which nobody ever uses other than Unix file permissions, use 0o123.Add missing Hash methodsRuby has a bad habit of treating parts of standard library as second class, and nowhere it's more mind boggling than with Hash, which might be the most commonly used object after String.It only just got transform_values in 2.4, which was probably the most necessary one.Some other methods which I remember needing a lot are:Hash#compactHash#compact!Hash#select_valuesHash#select_values!Hash#reject_valuesHash#reject_values!You can probably guess what they ought to do.Hash#zipTechnically Hash#zip will call Enumerable#zip so it returns something, but that something is completely meaningless.I needed it crazy often. With a = {x: 1, y: 2} and b = {y: 3, z: 4} to run a.zip(b) and get {x: [1, nil], y: [2,3], z: [nil, 4]}, which I can then map or transform_values to merge them in meaningful way.Current workaround of (a.keys|b.keys).map{|k| [k, [a[k], b[k]]]}.to_h works but good luck understanding this code if you run into it, so most people would probably just loop.Enumerable#count_byHere's a simple SQL:SELECT author, COUNT(*) count FROM posts GROUP BY author;Now let's try doing this in ruby:posts.count_by(&:author)Well, there's nothing like it, so let's try to do it with existing API:posts.group_by(&:author).map{|author, posts| [author, posts.size]}.to_hFor such a common operation having to do group_by / map / to_h feels real bad - and most people would just loop and += like we're coding in some javascript and not in a civilized language.I'm not insisting on count_by - there could be a different solution (maybe some kind of posts.map(&:author).to_counts_hash).URI query parameters accessRuby is an old language, and it added a bunch of networking related APIs back then internet was young. I don't blame anyone for these APIs not being very good, but by now they really ought to be fixed or replaced.One mindbogglingly missing feature is access to query parameters in URI objects to extract or modify them. The library treats the whole query as opaque string with no structure, and I guess expects people to use regular expressions and manual URI.encode / URI.decode.There are gems like Addressable::URI that provide necessary functionality, and URI needs to either adapt or get replaced.Replace net/httpIt's similar story of API added back when internet was young and we didn't know any better. By today's needs the API feels so bad quite a few people literally use `curl ...`, and a lot more use one of hundred replacement gems.Just pick one of those gems, and make it the new official net/http. I doubt you can do worse than what's there now.Again, I'm not blaming anyone, but it's time to move on. Python had urllib, urllib2, urllib3, and by now it's probably up to urllib42 or so.Make bundler chill out about binding.pryFor better or worse bundler became the standard dependencies manager for ruby, and pry its standard debugger.But if you try to use require "pry"; binding.pry some[...]



Celebrating Cloud's 10th Birthday With Cat Pictures

2017-06-25T04:50:24.685+02:00

Cloud is now a serious mature lady cat. Let's celebrate it the best possible way - by posting a lot of cat pictures!Young CloudIn her youth "smartphones" took potato-quality pictures which took a minute per picture to transfer over custom USB cables, so unfortunately I don't have many photos from that time.The few I've got shows she already had affinity towards computers, using laptop power supply as a chair:Cloud And ComputersCloud is indoors cat surrounded by computers, so she took a lot of interest in them.What a nice keyboard pillow:I wonder what keyboard pillow tastes like?Fixing Cabling:3D Printing:Using laptops as chairs:Hooman got me new laptop chair? Did it come in a box too?This laptop is weird, but if I fits, I sits:Doing Hooman ThingsCloud was curious about things hoomans do, so she sometimes trying to act like one, but eventually figured out it's much better to just be a cat.Drinking coffee:Standing on back paws:Packing for travel:Exploring Her WorldAs indoors cat she doesn't have far to explore but she's still doing her best.Going up:What is my hooman doing down there?Visiting vet:Keeping her fur snow white:Cloud and ChairCloud loves her hooman's chair. Especially when it's warm after use. There's just one problem - the hooman needs that chair too.Using chair as bed:Using chair for photoshoots:OK hooman, you can keep the big one, I like the cat-sized sidechair better anyway:Sleepy CloudAs a proper cat, Cloud loves sleeping on everything.Sleeping next to a computer:Sleeping in boxes:Sleeping on her cat tree:Sleeping on what her hooman is trying to read:Or write:Playful CloudUnfortunately it's hard to get a good pic of Cloud in action, as she's not a very active cat, and I don't have a Go Pro.Here's a photo of her catching a bird on a stick:Just being cuteLike any cat she loves just being cute:Happy Birthday Cloud![...]



CK2 to EU4 converter - how it works and how to mod it

2017-11-19T09:53:32.757+01:00

There's very little information about this wonderful thing, so after doing some research I decided to write it all up.What is a megacampaignParadox Grand Strategy games cover very long time, and most people never even "finish" a single game of CK2, or EU4 - but it's possible to go the other extreme and play a "megacampaign" - start in one game, then convert to another, and keep playing. Possibly even chain multiple such games, like CK2 to EU4 to Vic2 to HoI4.Since Paradox games make it hard to avoid blobbing, you pretty much have to play with mods, severe self-imposed restrictions, switch countries every now and then, or otherwise do silly things or your second/third/fourth campaigns will be pretty boring.Please don't ironman megacampaignsI'd generally dislike ironman, but it's even more important not to play ironman for a few reasons:Conversion process is never perfect, so you're very likely to want to tweak some things with console commands or save game editing before and after export.It's bad enough when game bugs out and you need to fix it by console, but when it happens to your campaign you've been playing for literally months, it can be really devastatingIf any of the games receive updates, you might need to do some fixing. CK2 is notorious for breaking saves any time trait list expands because they couldn't be bothered to include 10kB trait id to trait name mapping table in each save game.Ironman mode uses special save format which is pretty damn hard to "un-ironman" and edit if necessary.Megacampaign is not a meaningful achievement run - with so much time it's inherently going to be pretty easy past first 100 years, other than for self-imposed challenges with which ironman will be of no help.Or if you really want to, go ahead, just don't say I didn't warned you.What is the converterThere's been many converter tools which take save game from one game and create a mod for another.In theory you could convert save game to save game, but converting to a mod is much more flexible, so that's the generally used method. Mod setups starting map, rulers, and whatever else it felt like including.CK2 to EU4 converter is a builtin feature of Crusader Kings 2. You can only access it if you have converter DLC.If you want to continue to Vic2 and HoI4, you'll need to use third party tools.Where are the filesIf you want to mod it, or just look at it, you might be surprised. Unlike pretty much other DLC-locked content which is included in base game, just with some flags telling it to turn it off, all converter files are in DLC zip.Go to your Crusader Kings 2 game folder, find dlc/dlc030.zip, and unpack it somewhere.If you want to mod it, you can include modified files directly in your mod, in eu4_converter folder, just as if they were in base game.What converter doesConverter creates EU4 map matching CK2 map, with proper cultures, religions, dynasties, and government types, adding new countries as necessary.It creates matching rulers with sensibly mapped stats, traits, and heirs.It reassigns province development and centers of trade.In limited way it also sets up vassals.It will convert any game to 1444.11.11 game, no matter at which point you decided to use it.It will convert your stash of CK2 gold to EU4 gold at 10:1 rate, and your prestige at 50:1 rate.What converter doesn't doIt ignores any diplomatic relations or wars.It ignores any family relations, except for dynasty name. So if you're king of England and your son is ruling France, that won't be modeled in any way except giving you same dynasty.All provinces produce same goods as before (including gold), are in same trade nodes, have same estuaries, and other modifiers - wi[...]



opal-d3 for in-browser data visualization with ruby

2017-02-06T06:33:53.667+01:00

Ruby has always been the best language for server-side programming, but then if you want to do any dynamic front-end functionality your choices were mostly the awful javascript or one of compile-to-javascript languages like coffeescript, which were just borderline tolerable at best.Opal tries to fix all such problems this by compiling ruby directly into javascript. It has adequate javascript interoperability, so you can use existing javascript libraries with it, but it's often fairly awkward and non-idiomatic, negating a lot of value of using superior language.With opal-d3 I made intuitive ruby opal interface to D3 data-driver document visualization library. It's a great fit, as ruby is a great language for extracting and processing data, so it makes little sense to leave it for another language just for the final visualization step.To use it, just gem install opal-d3. The gem includes example sinatra app.What works and what doesn'tAbout 60% of D3 functionality is supported with nice ruby wrappers, including:arraysaxescollectionscolorsdelimiter-separated valueseasingsnumber formatsinterpolatorspathspolygonsquad treesrandom numbersscalesselectionsshapestime formatstime intervalsThe following parts of D3 are not supported yet. Of course you can still access them with somewhat low-level code:brusheschordsdispatchesdraggingforcesgeographieshierarchiesqueuesrequeststimerstransitionsvoronoi diagramszoomingSee it in actionI included a bunch of sample visualizations with the gem, which you can also view here.Future workPull requests most welcome. If you're interested in helping, some ideas are:It would be nice to have the remaining functionality - especially geographic code, and also wrappers for any popular D3 plugins.The interface tries to follows intuitive ruby style as much as possible, but there are some cases where it's not clear what would be the right way. I don't expect any major API changes, but some minor bits could change.It should generally be clear what ruby equivalent is, and there's plenty of specs and a bunch of examples in demo app, but there's no comprehensive documentation or tutorials as such.It would be good to just include more examples showcasing various included functionality - sort of like d3's homepage has. Especially examples showing dynamic interaction are lacking.If it becomes popular, the project could definitely use some better kind of home page on github pages.Unlike coffeescript which is pretty much free, opal comes with performance cost. opal-d3 right now doesn't try to minimize it, so it would be nice to have some benchmarks, and make sure it doesn't do anything unnecessarily costly.Included example sinatra app could definitely use source maps and in-browser ruby console for editing code without reloading.In case of troubleIf you have any questions, feel free to contact me over email, github, twitter, or whichever medium you find convenient.[...]



CK2 Prussian Amazons AAR

2017-02-05T05:26:13.918+01:00

Due to changes on G+, half of this AAR was originally published on G+, and it's saved below.The series continues on my other blog.Post 1 - Originally published on Google+ on 2016-12-25 06:17:18 UTCPrussian Amazons: Part 01: 769-774: Queen of LithuaniaHere's a fun idea - enatic tanistry. Starting as high chieftess of Prussia, only women of same dynasty can inherit, vassals vote which one.Actually I wanted to do mixed gender tanistry, but game engine won't allow that, so let's go all the way. Oh and I'm also using a mod that makes all vassals switch to liege's gender succession laws if they can, so it will be fun. Or at least weird. (Except it seems to not really work all that well so far, and so far all my vassals are agnatic-cognatic tanistry).I fully expect AI to have no idea whatsoever how to handle this, and a lot of console fixes to be necessary.Oh by the way I checked if I could make pagan women have male concubines, or multiple husbands, but game engine doesn't seem to support that. There's a "Male Harem" mod that fakes that with minor titles and events, but it changes a ton of files that other mods also change, and I don't feel like fixing all conflicts.And like 50 minor mods.Let's start with Mila Wilks, attractive, strong, gregarious, Midas Touched, 8/9/14/5/7, with +50% fertility bonus, and +2 health bonus, because this start is ridiculously easy to game over to RNG otherwise, and I'd rather add it upfront rather than reload a few times.First order of business is to imprison and execute that vassal who holds 2/4 of my counties, as apparently I don't yet have technology needed to pass title revocation. Then, get some tributaries to the West, subjugate Lesser Poland, get ambition to become queen of Lithuania, and subjugate my way there. No major blobbing here, just creating nice safe space.I found 18 year old strong attractive lowborn genius in Kiev, and I wanted to marry him, but he was a spymaster there, so I couldn't get him. Then he got captured by Magyars, and died in prison.Somehow khagan of the Magyars was willing to marry his son martilineally to me. That won't let my children inherit the horde for all the reasons, but it makes me really powerful.I got 4 children - 1 attractive girl Mila who's currently my heir, and 3 boys, one of which was son of high chief of Lesser Poland, and is currently ruling there after his father's accident. All of them are legitimized bastards, and my new husband is really unhappy about it.My vassals are all kinds of cultures and religions instead of being Prussian Romuva, so that needs to be taken care of sometime soon.I don't see much expanding in the near future. I just don't have CBs except for some tributary CBs on Finnish minors and Francia.Charlemagne got crowned emperor of Francia, so it won't gavelkind. Other than that, nothing interesting happened abroad.By the way the key to my strategy of blobbing early was simply inviting highest martial commanders in the world, 26 vs 8 martial is pretty good force multiplier. #ck2 Homeland of Prussian Amazons. Culture map in this part of the world is extremely fragmented.Post 2 - Originally published on Google+ on 2016-12-25 19:08:03 UTCPrussian Amazons: Part 02: 774-791: GlitterhoofI rechecked the mod that's supposed to make my vassals use same laws as me, and that only works for feudals, but there's no rush.I got into really bad tributary war with Norse blob - sadly my vassals and tributaries showed zero interest in any coordinated action, so I ended up white peacing Ragnarr Lodbrok - at least I killed his brother and sister in revenge. Without any other targets, that b[...]



magic/help for Ruby

2017-02-04T05:37:42.657+01:00


I've been doing some old code cleanup, and I've noticed that my magic/help irb plugin had many issues with ruby 2.x, and wasn't published for gem install magic-help - even though weirdly it was downloadable as a .gem file.

Both issues are now fixed. If you want to use it with 1.8, grab the old version. Otherwise gem install magic-help.

Oh and I replaced underscore with a dash to better match how dominant naming convention ended up being. The code is literally older than rubygems after all.

To be honest pry's builtin help is usually good enough, even though magic/help covers a few extra cases, I'm not entirely sure if it's still of much use.



Let's Play Organic Factorio

2016-12-23T01:51:01.973+01:00

I wanted to play some more Factorio - this time trying to use "organic" inspired design, like:
  • independent cells, with loops containing all materials inside
  • bloodstream where everything gets all ingredients, and into which all products are output
  • transport enzymes between cells
  • no dead ends - everything is just loops connected with other loops
And definitely no logistics robots (except for personal use) or massive buses.

To avoid wasting time, playing with no aliens, and Arumba's Accelerated Starts. Also Bob's mods to require a lot more complex networks of products from my organic factory, and increased saturation applied to make everything look better.

Here's full playlist which gets new episode once a day. First episode is here:

allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/Ks-e7hB_130/0.jpg" frameborder="0" height="360" src="https://www.youtube.com/embed/Ks-e7hB_130?feature=player_embedded" width="640">

So far it's been definitely challenging, and it forced me to try new and new designs as the organic factory evolves.

Full list of mods used:
  • Arumba Lights
  • Arumba's Accelerated Start
  • Auto Deconstruct
  • Auto Trash
  • Autofill
  • Bob's Adjustable Inserters
  • Bob's Assembling machines
  • Bob's Config mod
  • Bob's Electronics
  • Bob's Functions Library mod
  • Bob's Greenhouse mod
  • Bob's Logistics mod
  • Bob's Metals, Chemicals and Intermediates
  • Bob's Mining
  • Bob's Modules
  • Bob's Ores
  • Bob's Power
  • Bob's Revamp mod
  • Bob's Tech
  • Bottleneck
  • Bridge Railway
  • EfficienSee
  • Enhanced Map Colors
  • Flow Control
  • Foreman
  • Initial Scan
  • Larger Inventory
  • Long Reach
  • Peace Mod
  • Rail Tanker
  • Red Alerts
  • Research queue
  • Shuttle Train
  • Tree Collision
  • Upgrade Builder and Planner
  • Warehousing Mod
  • Yet Another Resource Monitor Fork
All mods available from in-game mod system.



EU4 Institutional Ethiopia AAR

2016-11-28T16:04:53.384+01:00

Post 1 - Originally published on Google+ on 2016-10-16 23:34:05 UTCInstitutional Ethiopia: Part 01: 1444-1454Campaign goals are:• enjoy Coptic mechanics (Fun and Balance Holy Site system turned off as they sort of duplicate each other)• enjoy institutions, maybe even without actually using development spam exploit• enjoy other new features like disinheritance, Great Power abuse etc.• remove kebabThe first order of business is that we have 6/5/6 righteous (+1 legitimacy) zealot (+1% missionary strength) with 1/2/1 heir, so straight for disinherit button.It all worked fine, as I ended up with 3/5/4 a few years later.Well, I start with one holy site, so first order of business was to go after second one. Coptic Alodia stood on a way and rivaled me, so I dispatched them on the way, getting some delicious PP, then I went after Shia Makuria, which didn't even have feudalism somehow.It was important than neither would piss off Mamluks too much, due to different culture/religion. All those lands up there were 1/1/1 high autonomy crap, but that gave me second Coptic holy sites - obviously first two blessings being coring cost discount, and missionary strenght.As soon as I got into war with Adal, taking advantage of temporary mil tech superiority, 13k and 14k rebel stacks popped up to mine 12k (my force limit is 18k, but I can't really get that many troops). Like, seriously Paradox, rebel stack sizes are ridiculous pile of bullshit - and of course they had tech 4, way ahead of most of Africa - and better generals.I was allied with:• Kaffa (but they rivalled me, so now they're gone)• Medri Bahri (still ongoing)• Yemen (still ongoing)• Warsangali (who suicided themselves into Adal in separate war while I was fighting them, and they're gone)Mamluks have 34k troops to mine 14k (and even that was only ramping up to fight rebels). What I'd really like is to just ally them until I get all East Africa, but that's unlikely to happen.Adal, Ajuuran, and Hedjaz are now best buddies, and all rivalled me, but I could only rival Ajuuran back - at least I'm at 100 pp thanks to all the eclipsing.I got a small gold mine from Kaffa, so that's something.Renaissance started in Milan. Not sure how I'm going to get it - usually Cyprus gets it, then Mamluks get it from Cyprus (as they're friendly), so it might reach me within sensible time frame. Of course I could also just exploit the development spam.Then again, I'm sort of tempted to dev spam my gold mine anyway.Alternatively maybe Ottomans (of whom I've never heard - it's fairly ridiculous how narrow my map knowledge is, and it's a while until I'll be able to steal maps) will go to war with the Mamluks, and then I can get some of their land for myself - but that would kill any friendly institution spread.#eu4Starting situationMissionary strategy - Alodia convince me not to take Beja by having active Coptic missionaryEthiopia strong, sort of. As long as Mamluks don't attack me I should be fine.Post 2 - Originally published on Google+ on 2016-10-17 08:48:25 UTCInstitutional Ethiopia: Part 02: 1454-1476I decided to not spam development to go around institutions, so I got:• sword tech 5 at 2% penalty• paper tech 5 at 3% penalty• bird tech 5 at 4% penalty• sword tech 6 was at 12% penalty (and one year ahead of time as I was overflowing on mana)• bird tech 6 at 13% penalty• paper tech 6 at 17% penaltyand institutions are just barely reaching Mamluk coast (due date - another century), but it wasn't via Cyprus as I expected.I got called into He[...]



Solving Self-Referential Aptitude Test with ruby and Z3

2016-12-02T02:51:13.496+01:00

Previously, I wrote how to solve sudoku and nonograms with ruby and Z3.Let's try something much more devious and complex - the Self-Referential Aptitude Test, a 20-question multiple-choice test where answers depend on other answers.By the way the test is totally solvable by pen and paper, I'd say it should take about half an hour, and no excessive backtracking.There's going to be a lot of code, but hopefully with my explanations it will all turn to be straightforward.Basic structureFirst, let's setup basic structure. Z3.Int("Q1") to Z3.Int("Q20") will be answers to the questions, with A being 1,  B being 2, C being 3, D being 4, and E being 5. I'm going to create a Hash storing question symbols, as we'll be using them a lot.To make the code read more like DSL than a blob of math, I'm going to define a lot of helpers.answer(10, "A") is expression stating that answer to question 10 is A.define(10, "A") { expression } means that answer to question 10 can be A only if expression is true. Note that this is implication, not equality, as multiple answers could be valid - see question 19 for most extreme example.require "z3"class SelfRefPuzzleSolver  attr_reader :q  def initialize    @solver = Z3::Solver.new    @q = {}    (1..20).each do |i|      @q[i] = Z3.Int("Q#{i}")      @solver.assert @q[i] >= 1      @solver.assert @q[i] <= 5    end  end  def answer(question_number, a)    @q[question_number] == " ABCDE".index(a)  end  def define(question_number, a)    @solver.assert answer(question_number, a).implies(yield)  end  def print_answers!    raise "Something went wrong" unless @solver.satisfiable?    model = @solver.model    (1..20).each do |i|      answer = " ABCDE"[model[q[i]].to_i]      puts "Q#{'%2d' % i}: #{answer}"    end  end  def solve!    # Question code goes here    print_answers!  endendCode below goes inside solve! method, except for helpers which all go inside the class.Counting answersMany questions require counting how many times something is true - mainly how many questions have specific answer.For this we convert Z3 Booleans (which are true or false) into 1s and 0s using statement.ite(if_true, if_false), and then add 1s together with Z3.Add. Z3.Add and friends aren't anything special, and you might just as well use .inject(&:+) etc. if you prefer.  def count(*statements)    Z3.Add(*statements.map{|b| b.ite(1,0)})  end  def count_answers(a, range=1..20)    count(*range.map{|i| answer(i, a)})  endQuestion 1The first question whose answer is B is question    (A) 1    (B) 2    (C) 3    (D) 4    (E) 5It's important to notice that answer like D means not only that "Q4 is B", but also that "Q1, Q2, and Q3 are not B".    define(1, "A"){  answer(1, "B") }    define(1, "B"){ !answer(1, "B") &                &nbs[...]



Let's Play Hearts of Iron 4 as Soviet Union

2016-11-17T07:57:21.598+01:00

Here's a series I wanted to try - as Soviet Union fight the whole world at once.

For this I modded in an event where every country declares war on me as soon as I finish the Great Purge. I wanted these to be separate wars, so I used mods to remove factions from the game. And to make it even harder, I played with no annexation rule, just puppetting. And a few other mods.

Unfortunately with so many wars ongoing and a butch of puppet countries, the series started suffering from horrible performance, and I had to stop early.

Anyway, you might still find it enjoyable - there's a lot of action until it gets too slow.

Here's episode 1. The rest will be published once a day.

allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/dOXW7ORywWc/0.jpg" frameborder="0" height="360" src="https://www.youtube.com/embed/dOXW7ORywWc?feature=player_embedded" width="640">


If you want to try such a thing yourself, you should probably remove the no puppetting rule, and that might just be enough to make performance tolerable.



Breadboard computer - switch board and diode board

2016-11-11T04:09:09.157+01:00

I gave it my first try a few weeks ago, but individual resistors were really messing with the build.So I ordered some 1k resistor arrays, and tried again.Full setupHere's the full setup - I'm using Raspberry Pi as USB adapter, getting GND and 5V out of it.Switch board needs both 5V and GND, while diode board only needs ground, as they all light up on positive signal.7 8-wide cables connect them, which is probably an overkill, but that's how many I could fit.Diode boardDiode board is made out of 7 identical 8-bit parts. From bottom up:8-bit signal input cable8 diodes (colors rotating between red, green, yellow depending on group)8-way 1k resistor arrayGroundDiodes were a bit too big to fit in line, but they actually look a bit better zig-zagging odd/even.I'm quite happy with this board.Switch boardThis is somewhat awkward build. There are also 7 identical 8-bit parts. From bottom up:5VA lot of cabling to get it all together - I really hate this part of the build as it makes everything super awkward just to do something as simple as connecting a bunch of pins together. One upside of soldering is that this kind of connections are super trivial to do.8-bit on/off switch8-bit signal output signal cable8-way 1k resistor arrayGroundSo when the switch is off, output is connected to 1k resistor to the Ground.And when the switch in on, output is connected directly to 5V.There are quite a few problems with this board.Max power dissipation here is rather large, with 56 wires of 1k resistance each, that's equivalent of just 18ohm, so that's 280mA or 1.4W just for one board. Using PC power supply that would be totally fine, but USB max power supply is just 500mA or 2.5W, so we're already stretching it.The most obvious solution would be to put much higher resistance, and fancier way would be to do so on both 5V and GND, and then use another board with a drivers for output (like 74HC4050).Another big problem is that switches are very hard to access - I basically need to use tweezers to flip them. The whole block of cabling on the bottom is extremely silly for what's basically "connect everything with everything" functionality.Even if that got fixed, inserting input/output cables is fairly awkward compared to my old (soldering-based) solution which used 40-wire IDE socket.Next stepsI could definitely improve these boards, but they ought to be good enough for now. Next step would be figuring out a way to interact with Raspberry Pi - I want it to be able to read signals from my switches and to send output to diodes.Raspberry Pi doesn't have anywhere near enough pins for it, so I might need some shift registers or other such chips to widen the interface.[...]



Solving nonograms with ruby and Z3

2016-12-02T02:59:50.371+01:00

Z3 is quite amazing, and whenever I show it to people on various unconferences response is very enthusiastic, but it desperately needs some tutorials.I wrote one for sudoku, but sudoku is maybe just a too straightforward example - variables are simply what goes into cells, and constraints are simply game rules. Usually you'll need at least a bit more modelling than that.So let's try something just a bit more complicated - nonograms:Nonograms are a puzzle where there's a grid of cells, either filed or empty.Every row and column has numbers describing cells as seen from its own perspective. So if a row has numbers "2 7" next to it, it means there's a group of 2 filled cells, and then (with at least one empty cell gap) another group of 7 filled cells.I'm going to refer to both rows and columns as "stripes", as logic for both is exactly the same, so it's simpler to just write it once.Get the dataSo the first thing we do is OCR that image... Just kidding, we'll transcribe it manually, as setting up proper OCR would take longer than that.Of course in a real solver these numbers would come from somewhere - probably an HTML scrapper, but it could really be OCR, or something else altogether.class NonogramSolver  def initialize    @row_constraints = [      [3],      [5],      [3,1],      [2,1],      [3,3,4],      [2,2,7],      [6,1,1],      [4,2,2],      [1,1],      [3,1],      [6],      [2,7],      [6,3,1],      [1,2,2,1,1],      [4,1,1,3],      [4,2,2],      [3,3,1],      [3,3],      [3],      [2,1],    ]    @column_constraints = [      [2],      [1,2],      [2,3],      [2,3],      [3,1,1],      [2,1,1],      [1,1,1,2,2],      [1,1,3,1,3],      [2,6,4],      [3,3,9,1],      [5,3,2],      [3,1,2,2],      [2,1,7],      [3,3,2],      [2,4],      [2,1,2],      [2,2,1],      [2,2],      [1],      [1],    ]    @row_count = @row_constraints.size    @column_count = @column_constraints.size    @solver = Z3::Solver.new  endendSetting up cell variablesZ3 variables are basically just Symbols with types, and Z3's boolean sort already has the right range, so we don't need to do anything special. A helper functionFor convenience let's write helpers to return row and column of such variables.None of these functions are strictly necessary and it's totally reasonable to do such calculations where they're needed.  def cell(x,y)    Z3.Bool("cell#{x},#{y}")  end  def row(y)    (0...@column_count).map{|x| cell(x,y) }  end  def column(x)    (0...@row_count).map{|y| cell(x,y) }  endExpress grid constraints as stripe constraintsEvery stripe (row or column) is independent, so let's write our grid constraints in terms of constraints over individual stripes.We're passing unique identifie[...]