<![CDATA[rolisz's site]]>https://rolisz.ro/https://rolisz.ro/favicon.pngrolisz's sitehttps://rolisz.ro/Ghost 3.15Sat, 04 Jul 2020 20:33:18 GMT60<![CDATA[Giving code presentations]]>I sometimes give talks at the local tech hub in the city where I live. It's not a big community, but I enjoy giving talks and they often provide a necessary deadline and motivation to finish some projects.

Last week I gave a talk about Rust. Given that there are

]]>
https://rolisz.ro/2020/07/04/giving-code-presentations-in-jupyter-notebook/5efb28a717253e7fe6dd646bSat, 04 Jul 2020 20:28:33 GMT

I sometimes give talks at the local tech hub in the city where I live. It's not a big community, but I enjoy giving talks and they often provide a necessary deadline and motivation to finish some projects.

Last week I gave a talk about Rust. Given that there are still some restrictions on how many people get be in one room, the physical audience was only 10 people, but there was a livestream as well.

Until now, I had used Google Slides for my presentation. For talks that don't have a lot of code, it works fine. But when you are presenting lots of code (such as a tutorial for a programming language), I found Slides to be lacking. If you paste in the code directly, you can't have syntax highlighting. You can paste in a screenshot, but then any later modifications to the slide mean retaking the screenshot and replacing it, so it's more work.

You can present in an IDE, but sometimes you want to have slides with normal text between pieces of code, where you explain some things. Switching between two apps can quickly get annoying. Also, it's hard to prepare just "bite-sized" content in an IDE, but that is needed so that the audience is focused only on what you are explaining right now.

So I decided to try something new for my intro to Rust presentation: I used Jupyter Notebook with a several extensions and I think it worked pretty well (except for a bug towards the end of the presentation).

For this I used the RISE extension, which adds live slide show support to Jupyter, using reveal.js. Each cell can be either a new slide, a sub-slide (so to get to it you have to "navigate down", in reveal.js style), a fragment (so it shows up on the same slide, but on a subsequent click), or notes. You can write new code and run it even during slideshow mode, so it's very useful if someone in the audience has a question, you can quickly write down and execute code to answer them. RISE is simple to install:

> pip install RISE

Then I used a bunch of extenstions that are bundled together in the jupyter_contrib_nbextensions package. By default, you have to enable and configure them by editing JSON files, but there is another plugin to add a dashboard for them, called jupyter_nbextensions_configurator. They can be installed with:

> pip install jupyter_contrib_nbextensions
> jupyter contrib nbextension install --user
> pip install jupyter_nbextensions_configurator
> jupyter nbextensions_configurator enable --user

You have to restart the Jupyter process and now you will see a new tab on the home page of the local Jupyter page, where you can enable and configure all the installed extensions.

Giving code presentations

I used the "Hide input" extension. Most of my code was organized into two cells. One which didn't contain all the code, just a snippet on which I wanted to focus (for example, I made a small change to a previously defined function), and another one which could be run and showed output. The latter cell was hidden with this extension, so that only the output could be seen.

Initially I also used the "Split cell" extension. This extension gives you a button which can make a cell to be half width. If two consecutive cells are half width, they align next to each other, making two columns. I wanted to use this to have code on the left column and explanations on the right column. This would have worked if the presentation was online only, because I wouldn't have had to zoom in too much. But because in the last week before the presentation we found out that it was allowed to hold the presentation in person (with 10 people in the audience) and I had to present on a projector and zoom in, I ended up removing all the split cells because the content wouldn't fit in any longer.

Making Rust work with Jupyter

All the above is generic and can be made to work with anything that works in Jupyter. To make Rust work in Jupyter you need a kernel for it. Some guys from Google have made one called evcxr_jupyter.

It's fairly straightforward to install. On Windows you need to first install CMake and then you run:

> cargo install evcxr_jupyter
> evcxr_jupyter --install

After restarting the Jupyter process, you now have the option of using a Rust kernel. To include Cargo dependencies, you can insert the following into a cell:

:dep reqwest = { version = "0.10", features = ["json", "blocking"] }

This downloads reqwest , compiles it and makes it available for using in other cells.

The notebook for presentation that I gave can be found in a Github repo and the recording can be found here.

I’m publishing this as part of 100 Days To Offload - Day 27.

]]>
<![CDATA[An update on 100 Days to Offload]]>It's been almost two months since I have started the #100DaysToOffload challenge. During this time I have managed to write 25 blog posts, which is more than I wrote last year in total.

But in June I found myself posting less and less often. What's going on?

Part of the

]]>
https://rolisz.ro/2020/06/30/100-days-to-offload/5ef7acb317253e7fe6dd6452Tue, 30 Jun 2020 12:22:58 GMT

It's been almost two months since I have started the #100DaysToOffload challenge. During this time I have managed to write 25 blog posts, which is more than I wrote last year in total.

But in June I found myself posting less and less often. What's going on?

Part of the reason is probably that the initial excitement wore off. I saw that sometimes I rushed posts in order to publish every day and I'd like to keep the quality high, especially for the tech posts.

But another reason is that the lockdown is pretty much over here in Romania and life started again. All kinds of events are happening, meeting friends, going out to eat and so on. All of these take a considerable amount of time, which squeezes out blogging.

The problem is not only time, but "communication" energy. I'm an introvert, so to recharge my batteries I need alone time. Lockdown was awesome for me, because my batteries didn't get as depleted. On the contrary, the lack of interaction with others started to bother me and I actually missed going out with others. But now that I do finally get to meet up with friends, I find that at the end of the day words don't flow out, even in writing. I guess blogging is similar enough to interacting with people that they tap into the same energy reservoirs.

And last, but not least, work on my house has resumed in (almost) full force and I have to start overseeing that. And it's a consuming job, because each contractor is saying that the previous guy didn't do a good job and they have to fix it. sigh I have to keep in mind what the end result will be and that it will be worth it.

So, in conclusion, I do plan to continue writing, but I won't write as often. My aim is to get around 7-8 posts per month, which should get me to finishing the modified version of the challenge (publishing 100 posts in one year).

I’m publishing this as part of 100 Days To Offload - Day 26.

]]>
<![CDATA[Pixel 3a]]>I've been a long-time fan of Google-made/branded phones. Back in 2014, I got a Nexus 5. Then I moved on to a 5X. Then I explored the other side, having an iPhone 6S for a year. I was not impressed by iOS, so then I switched back to Android,

]]>
https://rolisz.ro/2020/06/27/pixel-3a/5ef5097d17253e7fe6dd63f1Sat, 27 Jun 2020 20:24:18 GMT

I've been a long-time fan of Google-made/branded phones. Back in 2014, I got a Nexus 5. Then I moved on to a 5X. Then I explored the other side, having an iPhone 6S for a year. I was not impressed by iOS, so then I switched back to Android, this time using a Pixel phone.

I am aware that I often don't handle my phone with enough care, so I always buy a case for it. My Pixel was covered in a Supcase Unicorn Beetle case, which was bulky, but it managed to keep my phone intact, despite numerous drops (and throws). Until I took it out for 10 minutes and of course I cracked the screen. But the phone still worked, so I decided to hang on to it for as long as possible.

I somehow got used to the cracks on the screen. But the Bluetooth became flaky. I don't think it's from the drop, because there are plenty of online reports about others having similar issues. Eventually, it got bad enough that most of the time no combination of device restart/bluetooth restart/turning off wifi got my phone connected to either my earbuds or to my car. This meant that I couldn't listen to sermons while driving or while running, so I decided it's finally time to get a new phone.

The Google Pixel 3a has been receiving glowing reviews. It's cheaper than other similar phones, and it has one of the best cameras on the market (though only one lens, no telephoto or widelens). Some people don't like the fact that the back is made of plastic, but I don't care, because I'm going to keep it in a case anyway.

And it's pretty much what I expected: a better version of my previous phone. It looks almost identical. It doesn't have a notch. It still has a headphone jack. Screen is a bit taller. Supposedly it has a higher PPI, but who can tell at these counts? The CPU is faster, but I don't play games, so I'm not impressed. More storage space. And the camera is even better and this I care about. I haven't gotten the chance to take it out into nature (or even into the city), but backyard testing shows really good results. Both portrait mode and night sight are amazing, even though (or maybe because) they are software only, with no special hardware component for them.

One weird thing about the Pixel 3a is the "flexible" sides. When you squeeze the phone on the sides, it activates the Google Assistant. I found it too sensitive and sometimes I trigger it just by picking up the phone from the table. I don't use the Google Assistant anyway, so I'll just disable that feature.

I'm such a huge fan of the Unicorn Beetle cases, that I ordered another one for the Pixel 3a. Unfortunately, I couldn't find it in Romania, so I had to order from Amazon UK and it hasn't arrived yet :(

All in all, I am very happy with my new phone. I'm glad Google kept the winning formula and didn't introduce new designs just for the sake of having something new. I hope there will still be a Pixel 5a in 2-3 years, when I'll replace this one :D

I’m publishing this as part of 100 Days To Offload - Day 25.

]]>
<![CDATA[How Much Does It Cost To Run This Blog?]]>The current meme/trend/fad blog post topic in my blogroll is about how much it costs to run a personal blog.

So here are my costs per month:

Service Monthly Costs
Domain name 1€
VPS hosting 5.95$
DigitalOcean backup 1.19$
Total ~7.31€

Initially, I bought the

]]>
https://rolisz.ro/2020/06/15/how-much-does-it-cost-to-run-this-blog/5ee7b28417253e7fe6dd638aMon, 15 Jun 2020 18:19:45 GMT

The current meme/trend/fad blog post topic in my blogroll is about how much it costs to run a personal blog.

So here are my costs per month:

Service Monthly Costs
Domain name 1€
VPS hosting 5.95$
DigitalOcean backup 1.19$
Total ~7.31€

Initially, I bought the domain name from a Romanian registrar. Back then it was for life. I payed something like 30 euros for it. But 3 years ago, the powers that be decided to bring the .ro TLD to more "modern" standards and to have you pay yearly. I paid in advance for 10 years, 12 euros per year.

I have the smallest VPS from DigitalOcean to run my blog. I have another droplet which runs some other things, but I don't count that here. I also use their DNS hosting, which is free.

I also use their droplet backup service, which is 1 euro + VAT. It's the simplest form of backup and I should probably set up some higher level exports from Ghost to back up my posts.

I see that both Jan-Lukas and Kevin use a CDN. I haven't thought about using one so far, because I've been happy with performance so far. Serving static sites is really cheap. However, if I land on HackerNews again, I might need it...

Otherwise I use free tools to write my blog. Because I enjoy blogging, I also don't consider the countless hours sunk into this as a waste :)

I’m publishing this as part of 100 Days To Offload - Day 24.

]]>
<![CDATA[My operating system journey]]>Getting started with Linux

Ten years ago I was writing how I'm a big fan of Windows (and Android). I would regularly get into friendly debates with Cătălin, who was a staunch supporter of Linux. I kept dipping my toes into Linux, but for a long time, I got burned.

]]>
https://rolisz.ro/2020/06/14/operating-system-journey/5ec2eed817253e7fe6dd5a49Sun, 14 Jun 2020 18:46:40 GMTGetting started with LinuxMy operating system journey

Ten years ago I was writing how I'm a big fan of Windows (and Android). I would regularly get into friendly debates with Cătălin, who was a staunch supporter of Linux. I kept dipping my toes into Linux, but for a long time, I got burned.

At my first internship and then even more so at my first job, I learned more and more about Linux and got comfortable in it. I started dual booting. By 2014, the most used OS on my laptop was Fedora.

When I built a desktop in 2015, I first installed Linux on it, even though I had to try several distributions until I found one that worked. I was in my "command-line" minimalist phase, so I set up i3, tmux, and fish. I was quite happy with it, but eventually I installed Windows 10 on it so that I could play games, run the Swiss tax form application and YNAB, a budgeting app.

Trying out Mac OS

My work laptop at the time was a MacBook. I thought I would like it and I was looking forward to trying out all the cool and hipster apps that were only on Mac OS, such as Alfred. In the end, while working at Google, I used only a browser and a terminal, and I never got around to really work with any other apps, because I didn't need them. The terminal experience in Mac requires a bit more searching around to get things working. Macs come with old libraries out of the box, you have to update them using copy pasted shell commands and I managed to screw things up once with Homebrew.  I was not impressed by Mac OS and I didn't want to spend my own money on that crappy keyboard.

Slowly turning back to Windows

But Windows (and it's ecosystem) has changed a lot since then. When I bought my new laptop in 2018, it came with Windows and I never bothered installing Linux on it. Why? Windows Subsytem for Linux. You get pretty much all the CLI goodies from Linux and all the other nice stuff from Windows. For example, as far as I know, there's almost no laptop where Linux has comparable battery life with Windows and that is an important factor for me, because I work remotely.

On my desktop I still had ArchLinux, because running Tensorflow was easier on Linux than on Windows (modulo the Nvidia driver updates) . But slowly I got bored of the "command line" minimalism. I tried other desktop environments on Linux, such as KDE and Gnome, but they never stuck. KDE is too bloated, and I find the default theme to be outdated. Gnome looks nice, but I never got around to feeling comfortable in it. The others are too "fringe" for me and I think that it's too hard to find solutions to the problems that inevitably crop up, just because the community is too small.

For the last two months, I have found myself using almost only Windows, even on my desktop. This way, I can watch Netflix at the highest resolution (on Linux, you can watch only in the browser, where's it's capped at 720p), I can play games. Rust works just as well on Windows as on Linux. WSL satisfies my very few needs for Linux only apps. And I never had problems with Nvidia drivers on Windows (unlike on Linux). The new Terminal app on Windows is pretty sweet. Powershell is pretty cool too, even though I don't know much of the syntax so far.

And honestly, I just like the default UI on Windows more. 10 years ago I had the patience to tinker with themes and to customize my desktop endlessly, but now I don't have the time and energy to deal with that anymore. I see plenty of nice Linux themes on Reddit and I tried to replicate one, but abusing fonts to get some nice "symbols" in i3-bar? Ewww.

Even though many people complain about Windows updates messing things up, that has never happened to me in the last 5 years, even though I am running on the insider preview version of Windows 10. On the other hand, I did manage to screw things up with ArchLinux updates, but it was my fault usually, because I didn't read the instructions or I let too much time pass between updates.

Servers

That's the story for my desktops and laptops. On servers, it's Linux all the way. My NAS runs Linux. My VPSs run Linux. And I plan to keep it that way. But there I'm not bothered by the fact that I SSH in and do the half an hour at most every week from the command line.

The only thing that I didn't try was a variant of BSD. Five years ago I might have given it a shot, but now I don't want to relearn a lot of things, from command line flags to concepts like jails. The strongest argument for BSD would be security, but Linux is secure enough for me, for now.

The future

But who knows what will happen in the future? Maybe in five years I'll get bored again of Windows and I'll try something new. Maybe Fuchsia will become mature by then :D

I’m publishing this as part of 100 Days To Offload - Day 23.

]]>
<![CDATA[Playing Codenames with Rust]]>I love playing board games. I love machine learning. I love Rust. So why not combine them and do something really fun? So I ran a word vector analogy query and I got back:

machine learning + board games - Rust = Codenames 

So let's implement Codenames in Rust. It's a simple

]]>
https://rolisz.ro/2020/06/10/playing-codenames-with-rust/5ed5526217253e7fe6dd5eb5Wed, 10 Jun 2020 16:53:21 GMT

I love playing board games. I love machine learning. I love Rust. So why not combine them and do something really fun? So I ran a word vector analogy query and I got back:

machine learning + board games - Rust = Codenames 

So let's implement Codenames in Rust. It's a simple game. There are two teams, each having one Spymaster and several field operatives. There is a shared 5x5 board, which represents a map to the field operatives, which are hidden. Each spot on the map has a word on it. The spymasters must give clues to the operatives so that they can find the enemy operatives and take them out. A more detailed description and pictures can be found in my previous post.

In this post, we will implement a simple model for the game, using dummy agents. In a future post, we will implement some smarter agents, with word vectors.

Modeling the map

First, let's start modeling the map. Each cell on the map can be a red agent, a blue agent, an assassin (black) or a neutral character (gray). There is one word on each cell and we need to track if the cell has been overturned or not. All this will go in a file called map.rs:

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum State {
    Gray,
    Red,
    Blue,
    Black
}

#[derive(Debug)]
pub struct Cell<'a> {
    pub color: State,
    pub word: &'a str,
    pub revealed: bool
}

Cells have a lifetime parameter which is needed for the word field. Using the 'a lifetime parameter we tell the compiler that the str in that field will live at least as long as the cell that contains it.

While the derived Debug provides a way to print out the debug version of both the State and the Cell, when printing out in the CLI the actual game we'll want to customize what we print out. For this, we will have to implement the  Display trait, which is then used by formatters to create a string representation. For states, we will print only the first letters, for cells we will print the state if the cell has been overturned (so it's visible) and its word otherwise.

use core::fmt;
use crate::map::State::{Gray, Red, Blue, Black};

impl fmt::Display for State {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let char = match self {
            Gray => 'N',
            Red => 'R',
            Blue => 'B',
            Black => 'X'
        };
        write!(f, "{}", char)
    }
}

impl fmt::Display for Cell<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.revealed {
            f.pad(&format!("{}", self.color))
        } else {
            f.pad(self.word)
        }
    }
}

Now let's make a struct for the map. The map will have just a vector for the cells. We also want to provide a constructor, which in Rust is by convention called new, which takes a list of words, chooses 25 words randomly, and assigns each cell a color. Initially, all cells will have revealed flag set to false.

use rand::prelude::*;

pub struct Map<'a> {
    cells: Vec<Cell<'a>>
}

impl Map<'_> {
    pub fn new<'a>(words: &[&'a str]) -> Map<'a> {
        let mut colors = vec![Gray; 7];
        colors.append(&mut vec![Red; 9]);
        colors.append(&mut vec![Blue; 8]);
        colors.push(Black);

        let mut rng = thread_rng();
        colors.shuffle(&mut rng);
        let words: Vec<&&str> = words.into_iter().choose_multiple(&mut rng, 25);

        let mut cells = Vec::with_capacity(25);
        for i in 0..25 {
            cells.push(Cell { color: colors[i], word: words[i], revealed: false });
        }
        Map{cells}
    }
}

We have to have a lifetime even for map, because the cells need it. For the constructor, because we have two input lifetimes, we have to explicitly specify to which one is the output lifetime related: to the words, because the map will contain some of them.

For all the random stuff we use the Rand module. We import everything from it and then we instantiate a thread_rng. I find the name a bit misleading, because it has nothing to do with threads per se, it's just that it's not thread safe. Our program doesn't use threads, so we don't care about that. The Rand module then provides a trait for slices which we can use to shuffle the color list and to choose the 25 random words.

Let's also add a Display implementation, which will just show each cell, using the Display for Cell. To make sure things show up nicely in a grid, we calculate the longest word first and then we pad all the words to that size.

impl fmt::Display for Map<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let max_len = self.cells.iter().map(|x| x.word.len()).max().unwrap();
        for i in 0..5 {
            for j in 0..5 {
                write!(f, "{:width$} ", self.cells[i*5+j], width=max_len)?;
            }
            write!(f, "\n")?;
        }
        Ok(())
    }
}

The formatter knows to use the Display for Cell, so it will show words for unrevealed cells and colors for revealed cells.

Let's put everything together and test it out. Let's write a simple main.rs:

mod map;

use crate::map::{Color, Cell, Map};


fn main() {
    let words = vec!["RAY", "REVOLUTION", "RING", "ROBIN", "ROBOT", "ROCK",
"ROME", "ROOT", "ROSE", "ROULETTE", "ROUND", "ROW", "RULER", "SATELLITE", "SATURN",
"SCALE", "SCHOOL", "SCIENTIST", "SCORPION", "SCREEN", "SCUBA DIVER", "SEAL",
"SERVER", "SHADOW", "SHAKESPEARE", "SHARK", "SHIP", "SHOE", "SHOP", "SHOT", "SINK"];
    let map = Map::new(words);
    println!("{}", map);
}

I hard coded there a list of words for now, we'll get rid of that soon. For now, this should output something similar to the following:

RAY         SHOT        RING        ROBIN       ROBOT       
ROCK        ROME        ROOT        SHARK       ROULETTE    
ROUND       ROW         RULER       SATELLITE   SATURN      
SCALE       SCHOOL      SCIENTIST   SCORPION    SHOE        
SCUBA DIVER SEAL        SERVER      SHADOW      SHAKESPEARE 

Everything is nicely aligned.

The player agents

We have a map. Now let's start implementing the players. There will be two player types: a spymaster and a field operative. The spymaster has to give hints based on the map and the field operative has to guess words, based on the hint. In Codenames hints consist of a word (a single word) and a number, which is the number of words on the board that are related to the hint word. This goes into a players.rs file.

use crate::map::Map;

#[derive(Debug)]
pub struct Hint {
    word: String,
    count: usize,
}

pub trait Spymaster {
    fn give_hint(&mut self, map: &Map) -> Hint;
}

pub trait FieldOperatives {
    fn choose_words<'a>(&mut self, hint: &Hint, words: &[&'a str]) -> Vec<&'a str>;
}

The Hint word is a String and not a &str because in some cases we might have to return owned hints and not borrowed ones (such as when reading from the CLI).

Both the Spymaster and the FieldOperative must borrow self mutably, because they might need to change some state internally (such as a random number generator) to give their corresponding answers.

For now, we will implement two kinds of players: a random player, that outputs random clues and chooses words at random and a human player, that reads from the outputs from the keyboard.

use rand::prelude::*;

pub struct RandomSpyMaster<'a> {
    rng: ThreadRng,
    clues: &'a [&'a str]
}

pub struct RandomFieldOperatives {
    rng: ThreadRng,
}

impl RandomSpyMaster {
    pub fn new() -> RandomSpyMaster {
        let rng = thread_rng();
        RandomSpyMaster{rng}
    }
}

impl RandomFieldOperatives {
    pub fn new() -> RandomFieldOperatives {
        let rng = thread_rng();
        RandomFieldOperatives{rng}
    }
}

The random field operative has only a random number generator, while the spymaster also has a list of words from which it can choose clues.

impl Spymaster for RandomSpyMaster<'_> {
    fn give_hint(&mut self, _map: &Map) -> Hint {
        let word = (*self.clues.choose(&mut self.rng).unwrap()).to_string();
        let count = self.rng.gen_range(1, 5);
        Hint { word, count }
    }
}

impl FieldOperative for RandomFieldOperative {
    fn  choose_words<'a>(&mut self, hint: &Hint, words: &[&'a str]) -> Vec<&'a str> {
        let nr_found_words = self.rng.gen_range(1, hint.count+1) as usize;
        words.choose_multiple(&mut self.rng, nr_found_words).copied().collect()
    }
}

We do a dereferencing when getting the word in the spymaster, because Clippy says it's faster that way.

Let's test them out, by adding them in our main function:

    let mut sp = RandomSpyMaster::new(&words);
    let mut fo = RandomFieldOperative::new();

    let hint = sp.give_hint(&map);
    println!("{:?}", &hint);
    println!("{:?}", fo.choose_words(&hint, &words));

And we will have as output:

Hint { word: "SHIP", count: 4 }
["ROSE", "SERVER", "SHIP"]

It ain't much, but it's random work. Let's implement the CLI players, which are even simpler:

pub struct HumanCliSpymaster {}

pub struct HumanCliFieldOperative {}

They don't have any fields or internal state, because everything comes from the brain of the player.

The spymaster player should give the hint in the following format: 2 rust. If we can't parse it, we'll ask them to give the input again.

impl Spymaster for HumanCliSpymaster {
    fn give_hint(&mut self, map: &Map) -> Hint {
        println!("Give a hint for this map in count, word format: \n{} ", map);
        loop {
            let mut input = String::new();
            io::stdin().read_line(&mut input).unwrap();
            let results = input.split_ascii_whitespace().collect::<Vec<&str>>();
            match results[0].parse::<usize>() {
                Ok(count) => return Hint { count, word: results[0].to_string() },
                Err(_e) => println!("Give hint in count, word format!"),
            };
        }
    }
}

When reading from stdin, we apply unwrap. I can't even really imagine under what conditions that read might fail or what you could even do in that case, so I think it's fine.

The field operative has to give a list of as many words as the clue said, each on separate line. All the words they give must be from the words on the map.

impl FieldOperative for HumanCliFieldOperative {
   fn choose_words<'a>(&mut self, hint: &Hint, words: &[&'a str]) -> Vec<&'a str> {
       let mut chosen_words = vec![];
       println!("Choose {} words from {:?}", hint.count, words);
       let mut counts = hint.count;
       while counts > 0 {
           let mut input = String::new();
           io::stdin().read_line(&mut input).unwrap();
           if input.trim() == "" {
               return chosen_words;
           }
           match words.iter().position(|&x| {
               x.to_lowercase() == input.trim()
           }) {
               Some(c) => {
                   chosen_words.push(words[c]);
                   counts -= 1;
               },
               None => println!("Choose a word from the given list")
           }
       }
       chosen_words
   }
}

If the player submits a blank line, we return only the list of words we have found so far, even if they are fewer than what hint said, because they probably are out of ideas. If the player submits a word that is not on the map, we ask again.

To find the index of an item, the Rust idiom is to do vector.iter().position(|x| x == my_value), which returns an Option. I find it too verbose. Why isn't there a convenience indexOf method that directly tests for equality? I'm sure this is a very common use case.

The game logic

Now, let's put all of this together and make our game logic, in a file called game.rs. First we define a function that will do all the player interaction: getting hints and choosing words.

pub fn get_actions(spymaster: &mut dyn Spymaster, field_op: &mut dyn FieldOperative, map: &Map) -> Vec<String>{
    let hint = spymaster.give_hint(map);
    field_op.choose_words(&hint, &map.remaining_words()).iter().map(|&x| x.to_string()).collect()
}

The dyn keyword is necessary to make it explicit that we are using a trait object, which has some performance implications.

Next, we need to check the results. What happened in the last turn? If the player chose the Black cell, instant game over, they lost. If the player chose one of their own field operatives or a neutral one, their round is over and the remaining guesses are discarded.

fn opposite_player(color: Color) -> Color {
    match color {
        Color::Red => Color::Blue,
        Color::Blue => Color::Red,
        _ => panic!("Impossible player type!")
    }
}

pub fn check_if_lost(current_player: Color, guesses: &[String], map: &mut Map) -> bool {
    for word in guesses {
        let color = map.reveal_cell(word);
        if color == Color::Black {
            return true;
        }
        if color != opposite_player(current_player) {
            return false;
        }
    }
    false
}

We have also added a helper function to get the color of the other player. Now we need to reveal the cells in Map.

impl Map<'_> {
    pub fn reveal_cell(&mut self, word: &str) -> Color {
        let cell = self.cells.iter_mut().find(|x| x.word == word).unwrap();
        cell.revealed = true;
        cell.color
    }
}

To reveal a cell, we simply iterate over all the cells until we find the one where the word matches. We know the word comes from the cells, so it's safe to unwrap. We set the revealed bit to true and then we return the color of the cell, because that's all we need.

And now let's loop until the game is over.

pub fn game(red_spymaster: &mut dyn Spymaster, red_field_op: &mut dyn FieldOperative,
            blue_spymaster: &mut dyn Spymaster, blue_field_op: &mut dyn FieldOperative,
            map: &mut Map) -> Color {
    let mut current_color = Color::Red;
    loop {
        println!("The turn of player {}", current_color);
        let guesses;
        if current_color == Color::Red {
            guesses = get_actions(red_spymaster, red_field_op, &map);
        } else {
            guesses = get_actions(blue_spymaster, blue_field_op, &map);
        };
        println!("{:?}", &guesses);
        if check_if_lost(Color::Blue, &guesses, map) {
            return opposite_player(current_color);
        }
        println!("The map is now: {}", map);
        if map.is_over() {
            return current_color;
        }
        current_color = opposite_player(current_color);
    }
}

If the player lost (because they found the Black cell), we return the other player as the winner. If the game is over, we return the current player as the winner. Otherwise, we swap colors and it's the turn of the other player.

When is the game over? When there are no more cells belonging to red or blue that are unturned. To count the number of cells of a certain color that are unturned, we simply iterate over all of them and filter out the ones that have been revealed and the ones that are another color:

impl Map<'_> {
    pub fn is_over(&self) -> bool {
        if self.unturned_cells_of_color(Red) == 0 {
            return true
        }
        if self.unturned_cells_of_color(Blue) == 0 {
            return true
        }
        return false
    }

    fn unturned_cells_of_color(&self, color: Color) -> usize {
        self.cells.iter().filter(|x| !x.revealed)
                         .filter(|x| x.color == color).count()
    }
}

Our main function becomes as follows:

use crate::map::{Color, Cell, Map};
use crate::players::{RandomFieldOperative, RandomSpyMaster, Spymaster, FieldOperative, HumanCliSpymaster, HumanCliFieldOperative};
use crate::game::game;
use std::fs::File;
use std::io::Read;

fn main() {
    let mut file = File::open("resources/wordlist").unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let words = contents.lines().collect::<Vec<&str>>();
    
    let mut map = Map::new(&words);

    let result = game(&mut RandomSpyMaster::new(&words), &mut RandomFieldOperative::new(),
           &mut RandomSpyMaster::new(&words), &mut RandomFieldOperative::new(), &mut map);

    println!("The winner is {}", result)
}

I've also moved the list of words to an external file, so that they are not hardcoded in the binary. You can find a list of all the words in the official game with a quick DuckDuckGo search :D

If you want, you can try to put a HumanCliSpymaster or a HumanCliFieldOperative and see if you can beat the random player. It's quite easy :)

The whole code put together can be seen on GitHub (on the blog branch).

Now, we've got the basic skeleton for our Rust implementation for Codenames. Next time, we will get to the fun part: creating a smart agent that actually knows how to give hints and how to make guesses, using word vectors.

I’m publishing this as part of 100 Days To Offload - Day 22.

]]>
<![CDATA[Boardgames Party: Codenames]]>Lockdown is over, time to play some games that work well in larger groups, such as Codenames, which is a very fun game to play, but it requires language skills. On the box it says it can be played by 4-8 players, but I think you can easily have more

]]>
https://rolisz.ro/2020/06/08/boardgames-party-codenames/5ede7a0a17253e7fe6dd61e4Mon, 08 Jun 2020 21:05:00 GMT

Lockdown is over, time to play some games that work well in larger groups, such as Codenames, which is a very fun game to play, but it requires language skills. On the box it says it can be played by 4-8 players, but I think you can easily have more people.

Boardgames Party: Codenames
The map as seen by the spymasters

There are two teams, one red and one blue. Each team has a spymaster and several field operatives. There is a 5x5 map, where each cell can be a red agent, a blue agent, a neutral bystander or an assassin. The goal of each team is to locate all the enemy agents. Only the spymasters see the map with the color coded cells.

Boardgames Party: Codenames
The map with words

The players see only the map with words overlayed on cells. In the map above, the top left cell is the assassin (black card) and has the word worm on it, while the bottom left cell has a red agent and the word box.

The spymaster must give out clues so that the field operative can figure out where are the enemy agents. The clues must consist of a single word and how many agents can be found with that clue. Obviously, you cannot use any words that are on the board, or that are plural or singular versions. For example, for the above board, a valid clue from the blue spymaster might be iron 3. If the field operatives guess correctly, the board might look like this afterward:

Boardgames Party: Codenames
After guessing that mine, hook and nail are related to iron

Now it's the other team's turn and the red spy master could give a clue such as restaurant 3. The field operatives guess correctly 2 of the 3, and find a neutral bystander too.

Boardgames Party: Codenames

If while guessing, a team flips an agent of their own or a neutral bystander, their turn is over and they forfeit their remaining guesses.

This keeps going until either one team finds all the enemy agents, in which case they win, or until they find the assassin, in which case they lose.

Codenames it's a really fun game, even though it's simple to explain the concept. As a spymaster, it's really frustrating to see that the clue you spent so much time to find is being completely misinterpreted by your team and you listen in horror how their speculations get closer and closer to the assassin. As a field operative, it's really fun to argue with the others what could the spymaster have meant. Maybe box is related to restaurant, because some restaurants have bento boxes.

One slight problem with the game is if you play it in a game which is not everyone's native tongue. Then some problems might arise because some people don't know the meaning of the word. But the game has already been translated to several languages and there is even a picture version of it.

Score: 10

I’m publishing this as part of 100 Days To Offload - Day 21.

]]>
<![CDATA[Happy decennial anniversary!]]>Well, this has happened too: ten years of blogging. I feel like it's only yesterday that I wrote the 5 year anniversary post. Some things have changed in the meantime, some haven't. I still enjoy writing, but my goals about my blog have changed.

10 years ago I don't think

]]>
https://rolisz.ro/2020/06/08/happy-decennial/5eddf7aa17253e7fe6dd617eMon, 08 Jun 2020 10:26:34 GMT

Well, this has happened too: ten years of blogging. I feel like it's only yesterday that I wrote the 5 year anniversary post. Some things have changed in the meantime, some haven't. I still enjoy writing, but my goals about my blog have changed.

10 years ago I don't think I knew what I wanted from my blog. I posted lots of content, some well thought out, some not at all. Five years ago, my blog was mostly a way to keep in touch with friends and family. Now, I want to expand my reach again. I've made it to the front page of Hacker News this year, with the Moving away from GMail post. That got me 30 thousand page views in three days, more than I had last year in total. The Rust Web Crawler post was also quite successful, getting to more than 1000 views.

This year I have already posted more than in the last six years (this would be the 33rd post in 2020), mostly thanks to joining the 100 Days to Offload and I hope that at least for some of those posts, the quality is much higher than before. In particular, I pay a lot more attention to the order of ideas, I try to group them well, to add headers and so on. For longer pieces, I also ask for feedback from some friends.

As always, I continue having big ideas for my blog. I hope you'll continue reading it, for the next ten years as well!

I’m publishing this as part of 100 Days To Offload - Day 20.

]]>
<![CDATA[New desk setup]]>Last week we were at my in-laws for a couple of days. We got back home late on Thursday. As I went upstairs, I took my backpack containing my work laptop to drop it off in my home office. I didn't bother turning on the light in the office, because

]]>
https://rolisz.ro/2020/06/07/new-desk-setup/5edc88c317253e7fe6dd610fSun, 07 Jun 2020 07:34:54 GMT

Last week we were at my in-laws for a couple of days. We got back home late on Thursday. As I went upstairs, I took my backpack containing my work laptop to drop it off in my home office. I didn't bother turning on the light in the office, because I wanted to only put down the backpack and then go to bed. I dropped it off and I wanted to leave the room when I stopped in my tracks. Something was off.

My desk had been a darker cream color, but now there was a white desk in its place. I pivot and take another look, while still in the dark. I turn around and look at my wife who was following close by: she's recording me and she starts laughing.

Turns out she and her family pulled off the best gift surprise for me ever. For legacy reasons (read: was too lazy/cheap), my old desk was actually an IKEA kitchen table, that I had brought back with me from Switzerland. I had bought a big office chair here in Romania, but I never liked it too much. Knowing this, my wife and my in-laws ordered me a new BEKANT desk and a JÄRVFJÄLLET chair from IKEA. They arrived while we were at my in-laws, so she asked a neighbor to pick them up and place them in our house. My brothers-in-law came over one evening and assembled them, taking care to preserve the mess that is on my desk and not cleaning it up. Well organized, my dear wife!

After 2 days of using, I can say I really like my new desk and chair :D It was about time I upgraded to a desk whose height I can adjust.

And I'll end by paraphrasing Homer Simpson:

New desk setup

To many more gifts like these!

I’m publishing this as part of 100 Days To Offload - Day 19.

]]>
<![CDATA[Quarantine boardgames: Pandemic]]>The pandemic is "over" in Romania (or at least, the most severe lockdown is over), so it means we won the game of Pandemic :)

Pandemic is a game were there are four diseases ravaging through the world. The players must cooperate to prevent outbreaks and to find cures for the

]]>
https://rolisz.ro/2020/06/02/quarantine-boardgames-pandemic/5eb3ab9181fef554fabb4208Tue, 02 Jun 2020 19:24:43 GMT

The pandemic is "over" in Romania (or at least, the most severe lockdown is over), so it means we won the game of Pandemic :)

Pandemic is a game were there are four diseases ravaging through the world. The players must cooperate to prevent outbreaks and to find cures for the infections.

Quarantine boardgames: Pandemic
The little cubes represent diseases

To find a cure, one player must gather five city cards of the disease's color and then travel to a research station in order to prepare the cure. Each player receives some city card at the beginning of their turn, but that's rarely enough, so they have to trade cards among each other. Cards can be traded only in their corresponding cities, so a lot of travel is involved. If the city cards run out, the game is over.

Quarantine boardgames: Pandemic
Epidemic cards give you anxiety

On each turn, more and more cities get infected. If a city has three diseases cubes and another one should be added, there is an outbreak there. The disease spreads to all the surrounding cities. If 7 outbreaks happen, the game is over.

If there are not enough disease cubes to place on the map, the game is over.

Quarantine boardgames: Pandemic
The little vials represent the cures for diseases

There are many ways to lose the game of Pandemic and only one to win: you must find the cure for all the diseases, but not necessarily eradicate them. The first several times I played we lost the game. The last time I played the game was when the COVID 19 pandemic was already starting, and even though I played with Catalin and we drew diagrams and tried to optimize the last 10 actions, we still lost.

Each player has a different card. You can be a scientist and then you need only four city cards to research a cure. You can be a quarantine specialist and then you prevent the placement of disease cubes around you. You can be a medic and then you can remove all disease cubes with a single action.

Quarantine boardgames: Pandemic

The game is a cooperative game, so you share victory and defeat. It's one of the first well known coop game, so it has the problem of quarterbacking: some players who are more assertive easily end up telling everyone what to do, so it requires restrain on their side.

The game is a great way to learn geography: this is how me and my wife now know the biggest cities in Asia and Africa. How else would you know where Kinshasha is?

Quarantine boardgames: Pandemic

Pandemic is a really fun game. And rumour has it that the legacy versions are among the best board games ever. But I haven't found yet the board game crew to play with!

Grade: 9

I’m publishing this as part of 100 Days To Offload - Day 18.

]]>
<![CDATA[Yard work]]>Spot camouflaged Roland in the above picture

I've spent two Saturdays in the past month doing yard work at my grandparents house. They are still alive, but they are staying at an aunt right now, so nobody has been taking care of their house since fall. Me and my dad

]]>
https://rolisz.ro/2020/06/01/yard-work/5ed3508d17253e7fe6dd5df2Mon, 01 Jun 2020 09:57:38 GMT

Spot camouflaged Roland in the above picture

I've spent two Saturdays in the past month doing yard work at my grandparents house. They are still alive, but they are staying at an aunt right now, so nobody has been taking care of their house since fall. Me and my dad got yard duty, while my wife and my mom cleaned up inside.

I've lived most of my life in an apartment, so this has been quite an educational experience for me, and also a good way to do some physical exercise.

While I learned a while ago how to cut grass the old school way (with a scythe), I still have some things to learn, because I managed to break grandpas scythe :/ New things I've learned: climbing trees and pruning them.

There was another interesting insight that I gleaned while carrying various old things out, mostly to take them to the garbage dump. My grandfather pretty much never threw out anything: we found an old fridge, several ancient stoves, random rusted sheets of metal, all of which had to be taken to the scrap yard. My dad's explanation is that this was a useful behavior back in communist days, because then it was really hard to buy many things, so you kept things, just in cause you might need them for something. However now, when there are plenty of construction material retailers selling anything you can possibly want, this is no longer a useful trait and leads just to an accumulation of junk.

I wonder how things will evolve in the future. A trend today is towards minimalism, to own as few things as possible. This is possible because of how easy it is to buy or rent anything with almost immediate delivery, so in many cases it doesn't make sense to have many items just cluttering up your house. But what will happen if supply chains get disrupted, because of a pandemic or because of a trade war with China, and there will be a scarcity again about many products? Will my grandfather's mentality become the fitter one?

Another thing that I wonder about is what other adaptations my generation has, which in 30 years will look completely obsolete? Will COVID-19 leave a big mark in the collective psyche of the millennials and will my grandchildren wonder why grandpa always has a mask in his pocket?

I’m publishing this as part of 100 Days To Offload - Day 17.

]]>
<![CDATA[DuckDuckGo]]>Some of my more astute readers might have noticed the "word" "DuckDuckGo" appearing several times on my blog this year, both as a noun and as a verb. Contrary to how the name sounds, it's not a board game; it's a search engine. It's an alternative to Google.

DuckDuckGo promises

]]>
https://rolisz.ro/2020/05/26/duckduckgo/5ecd754317253e7fe6dd5d9eTue, 26 May 2020 20:52:41 GMT

Some of my more astute readers might have noticed the "word" "DuckDuckGo" appearing several times on my blog this year, both as a noun and as a verb. Contrary to how the name sounds, it's not a board game; it's a search engine. It's an alternative to Google.

DuckDuckGo promises to be more privacy friendly. They say they don't track you and they don't show targeted ads, only keyword related ads.

Below are the results if you search for me on DuckDuckGo and on Google:

Google has a bit fancier organization of my blog content. But actually DuckDuckGo has only my accounts on the first page, while Google quickly veers off to showing links of other people, such as a LoL gamer.

One of the really cool features of DDG are the bangs. If you enter !g in your search query, DuckDuckGo will redirect you to search on Google (supposedly with some fewer cookies). yt will search on YouTube, gm on Google Maps and so on. They have several thousand bangs already. This is actually a time saving feature, because you can search directly from the address bar, without having to go to that other site first. And when DDG search results are not good enough, you can just add a bang and search on Google.

DuckDuckGo

Another cool feature for programmers: DDG has much better StackOverflow integration. When you search for something programming related, they often show a snippet on the side with the top answer from StackOverflow. It makes copy pasting sooo much easier.

If you think it's funny that an ex-Googler uses DuckDuckGo, you should have seen the faces of my colleagues at Google when they saw that I was using DDG at Google. And even funnier was when my manager noticed this and then we had a discussion about why he doesn't use it: local queries (searching for nearby restaurants for example) were not working very well in DuckDuckGo and he used them very often. Well Suman, if you are reading this, DuckDuckGo now has you covered on that front too.  

If you are interested in alternatives to the big tech companies, I highly recommend using DuckDuckGo.

I’m publishing this as part of 100 Days To Offload - Day 16.

]]>
<![CDATA[Four months of Bullet Journaling]]>I'm using diary and journal as synonyms. Some people say they are the same, others say there are differences between them.

I started keeping a journal when I was in elementary school. I don't remember what made me start keeping a journal. It was probably the fact that my dad

]]>
https://rolisz.ro/2020/05/24/bullet-journaling/5eb1884881fef554fabb407bSun, 24 May 2020 19:52:23 GMT

I'm using diary and journal as synonyms. Some people say they are the same, others say there are differences between them.

I started keeping a journal when I was in elementary school. I don't remember what made me start keeping a journal. It was probably the fact that my dad had many notebooks. What I found funny was that he used notebooks with the year embossed onto them, but he never used one from the current year, always using an older one.

Four months of Bullet Journaling
I must have gotten bored halfway through drawing the fireworks

The initial entries in that diary were elementary. They had quite short notes about  my day and they had drawings. One that stands out is the drawing of the fireworks from New Years Eve. I often forgot to write, so there were long gaps between entries. The first entry is from the 21st of November, 2000, and the last one is from the 3rd of August, 2009, so a long time covered in that small 44 sheet notebook.

Moving to the digital world

I kept writing in a journal in other notebooks. Eventually, I moved to the digital world, continuing to write in text files on the computer. There I tried different methods of writing. Sometimes I would write only when something out of the ordinary happened. Sometimes I wrote every day, but only bullet points of everything that I did.

One method that I stuck to for 5 years was using a command line tool called jrnl and using Beeminder to remind me to write twice a week. But that method had some drawbacks: where to keep the journal file. Initially, I had it on my desktop. While I was at Google, that was fine, because I had only my desktop as a personal computer. But after I came to Romania, I had both a personal laptop and a work laptop and I would have to meet with the rest of my team about twice a month. So the journal file living on my desktop was not accessible anymore. I don't like the idea of putting my private journal file on some random server, even if I encrypted it. I could have placed it on my NAS, where it's still under my physical control, but then I slowly got bored of writing it from the command line, so I kinda stopped writing in my journal for a while.

Starting a Bullet Journal

Then this year in January I decided I'll go back to analog. I bought a nice dotted Moleskine notebook. I bought a fancy Faber Castell pen. And I decided to start bullet journaling.

There are a million variations on how to write a bullet journal out there, including things like the picture from the top of my post. I am definitely not doing that. What I want from a bullet journal is the following:

  • a place where I can write down what is going on in my life;
  • a place to keep track of my todos;
  • a way to track some habits;
  • a brewing pot for projects, while they are still in the idea phase.  

Process

The index

Four months of Bullet Journaling

The first couple of pages of my journal are reserved for the index. Here I jot down where I wrote about various topics. I write things like what page I started a new month, where I wrote about some projects and so on.

This is a way to help me find things if I have to look them up.

Monthly planner

Four months of Bullet Journaling

I have two pages at the beginning of each month. One is a list of all the dates, where I can write if I have todos that I should do on a certain date. Here I also have some columns where I can tick if I have done a certain habit in that day. In the picture above, I have two habits listed: doing some physical exercise and calling people to keep in touch with them during the lockdown.

On the next page is a list of notes about the month or any kinds of larger tasks that I want to accomplish during that month. For example, I would write that in a certain month I want to finish the electrical system for my house. This will then get broken down into smaller parts, such as finding a contractor, deciding where all the outlets are and so on. But having that task there helps me know what to focus on during this month: I have to figure out the electrical system.

Daily log

I don't actually do bullet points, because I like how words flow on paper, so I tend to write full sentences. The length of daily entries varies, from 1-2 paragraphs to 1-2 pages. For some entries, I draw little symbols at the beginning of the paragraphs as a sort of topic. For example, when I write down what passages I read from the Bible, I put a little menorah before them 🕎. Or at least I try to. On some days it looks like anything but a menorah.

The encryption I use is my ugly handwriting. Sometimes even I have problems decrypting it.

Project/ideas pages

Whenever I have some new information that I want to jot down, I put a heading on the next page and then write it down. For example, I talk to a contractor about some work on the house. I write down the details, costs, materials and so on. Then I add the page title and number to the index page at the beginning of the notebook, so that I can find it again easily, without having to look through the whole notebook.

Other people have even other kinds of entries, such as future logs or weekly planners. I haven't felt the need for that yet.

Advantages of writing in a notebook

Previously, I would do all these things on a computer. Doing it on a computer has its advantages: it's much easier to search for something; it's easier to schedule recurring tasks; it's easier to automate the recording of some habits. But there are some advantages to doing them with pen and paper.

For the diary part, I believe the most benefit comes not from being able to search through it, looking for something in particular, but from randomly browsing through it. And this can be done just as well (or even better) in a notebook. By looking through it randomly, you can be reminded of things that have completely slipped your mind and now you can make new connections.

For example, at some point, I was unsatisfied with the project I was working on. I started reminiscing how much better it was when I was working at Google. But then one day I randomly looked through my journal and I noticed that I had had the exact same problem at Google too: I had written down how I had complained to my manager about my current tasks and how I got told to stop whining and get it done. Being reminded of how this had happened even with my best manager to date, Regina, helped put the current problems in a better perspective and helped me find a solution.

Todos are another thing that for me work better on paper. Previously I used Todoist on my computer to keep track of todos. But the list of tasks grew without bounds. I would add there random ideas that I had, and I would never get around to doing them. The oldest task I have there is about 2 years old. I was probably using the wrong process for my todo list, dumping things there that should be placed somewhere else. But I find that by writing things down on paper, on the page corresponding to the day when I should do things, I have to think better about my todos. If I keep postponing something, I have to copy it over and over to a new page. Eventually, this forces me to either do it or to realize it's not going to happen and then I can delete it or move it to a "Some day when I'll have even more time than during a pandemic lockdown" list.

The monthly planner is not necessarily an advantage of pen and paper writing, but of the bullet journaling method. Taking the time to think what you want to achieve in the next time period is extremely beneficial. If you take the time to also reflect back on the previous month, see what you managed to do and what went sideways, you can get insight into your life and notice if things are going according to plan or if things are getting sidetracked. This can show you that maybe you need to prioritize some things and allocate more time to them.

Another benefit is that pen and paper don't distract me. When I was writing on my computer, I would often start to do other things. I would start writing in my journal and then I would check social media, read some news, it would get late, so I would just jot down a few lines and go to bed. With the notebook, I can take it to bed and offload my thoughts there. There is no risk that it will keep me up late, unlike my phone.

The future of journaling

So far I have filled 144 pages of my 400 page Moleskine notebook. I will try to continue writing at least until I fill it up. Afterwards, I will have to see if I continue writing. If I do, I will look for another notebook, because I am not too impressed by Moleskine. I've heard good things about Leuchtturm notebooks, so I'll probably try them as well. But I recently stumbled unto some really neat note taking apps... so we'll see what happens in a couple of months.

I’m publishing this as part of 100 Days To Offload - Day 15.

]]>
<![CDATA[About time]]>

Time, or at least our perception of it, is a very interesting thing. It's not only in Einstein's relativity theory that time is relative. I believe most humans feel that days go by slowly, while years fly by. As you toil away at work every day, it seems that 5

]]>
https://rolisz.ro/2020/05/20/about-time/5ec5763917253e7fe6dd5abdWed, 20 May 2020 19:52:16 GMT

Time, or at least our perception of it, is a very interesting thing. It's not only in Einstein's relativity theory that time is relative. I believe most humans feel that days go by slowly, while years fly by. As you toil away at work every day, it seems that 5 o'clock never arrives so you can go home from the office, but at the end of the year, when you look back on the past 365 days, it feels like you barely did anything. And of course, the length of a minute is very different, depending on which side of the bathroom door you are.

Back in my dad's days, in 1978

The past

I believe this is partly because how our brain stores memories. Similar things get grouped together, while differences are made to stand out. When we are children, almost everything is new, so it seems that so many things happened to us. But when we are adults, our normal weekdays are pretty much the same, so the brain won't waste much space to store information about them, and because of this we won't have much to remember looking back. This means that one way to make our years seem longer is to do all kinds of diverse activities, to try out new things and have novel experiences. Easier said than done during lockdown.

Another funny thing about the past is that it often feels like it was better. How many people say wishfully "Back in my day..."? Even I say it, when I see my sister-in-law studying programming and struggling to understand the modern technology stack, where you have to know React, Javascript, SASS, CSS, HTML,  Webpack, node.js, Mongo DB, Redis, nginx, Docker, HTTPS, CORS, ELK and even Kubernetes if you are particularly unlucky, just to deploy a website, while back in my day it was so much easier to get started, knowing only PHP, MySQL, HTML and CSS. But then I remember that there was no StackOverflow back then, so it was almost impossible to find an answer why the interpreter throws an error. You had to wade through pages and pages of dubious forums, where most posts were unrelated to the original post.

This is due to another bias in our brain, which tends to remember only the extremes. We remember either only the best things (games were more fun when I was growing up), not remembering the bad parts (how much I tried to make some of them work or how I had to search for license keys for them). Or we remember only the bad things (how I disliked driving and nearly had an accident on my very first day after I got my driving license), and we forget the uneventful parts (how, with God's help, I never actually got into an accident).

Fast forward to my kids days: Photo by Lucrezia Carnelos / Unsplash

The future

When looking forward to the future, I am usually impatient. I often have many ideas I want to try out, many new things I want to learn, many new places I want to visit. If I think about doing a Master's degree, it seems like two years is such a long time. If I calculate how long it would take me to finish a certain project and the result is several months, I am almost discouraged from starting it.

When I have an idea, I want to get started on it right away. Then I get bored of it in a couple of days, without having finished that project, so I just abandon it. I have so many of these programming projects on my hard drive. My notes are even fuller with ideas that I never got around to trying out.

When I need something, I want to buy it or acquire it right away. Then after I buy that gadget, I find that it's not even that useful. Very few are the cases where I spend a long time to do research before I buy something, but then I usually enjoy it much more.

When I think of my free time, I think it's so little. I want to do so many things, but it feels like every day (or maybe not even every day) I can spend only half an hour on some of them. I think I will never get around to finish them.

But lately I've been observing that slow and steady does get there. With my house construction? I thought the paperwork would never arrive last year. But the walls are up, and now I'm already in discussions with several contractors about the interior plastering of the walls and the exterior thermal insulation. Some of the contractors tell me they can only come and do the job in one month, while others even later, only in September. My first reaction is that "So late???". But then I try to think about the last year and how slowed everything seemed to go, and yet here I am. Next thing you know, it will be next year and I'm tinkering with my networking and home automation.

It's the same thing with learning Rust. I have wanted to learn Rust for several years now. I got around to actually starting a tutorial last year in September. I finished two-thirds, then I got busy with other things and had to put it on pause. I started again in November. Then I wrote the web crawler in Rust. Then again a pause for a while. At the beginning of March, I started another project in Rust. Sometimes several days go by without having any time to work on it. But now I have a working base and the fun can start. And I learned a lot of Rust in the meantime. Do I still have a lot to learn? Absolutely! Will it take a long time at this pace? Of course. But will I get there? I really hope so.

I believe this is true in all domains in life, be it physical (losing weight, gaining strength), mental (learning new things), emotional (understanding yourself better) and even (or especially) spiritual things (understanding God better). So I continue to try to make a small step forward every day, not getting discouraged on days when I don't see progress or when it seems it was better before, but continuing "to press on toward the goal for the prize of the upward call of God in Christ Jesus." (Philippians 3:14)

I’m publishing this as part of 100 Days To Offload - Day 14.

]]>
<![CDATA[Bridging networks with a Synology NAS]]>Warning: potentially bad idea ahead

For various reasons, I had to reorganize my working setup at home, including moving my home office to the room where my Synology NAS is. In this room, I had only one Ethernet outlet, but I needed two connections, one for the NAS and one

]]>
https://rolisz.ro/2020/05/19/bridging-networks-with-a-synology-nas/5ec433a417253e7fe6dd5a50Tue, 19 May 2020 20:25:07 GMT

Warning: potentially bad idea ahead

For various reasons, I had to reorganize my working setup at home, including moving my home office to the room where my Synology NAS is. In this room, I had only one Ethernet outlet, but I needed two connections, one for the NAS and one for my desktop. I was too lazy to go to the nearest electronic shop to buy a switch this evening and I didn't want to unplug the NAS, but then I had an idea:

My NAS has two Ethernet ports. What if I use it as a sort of router, connecting the second Ethernet port directly to my desktop?

Let's give it a shot. I connected the desktop to the NAS. Both of them perceive that there is something connected, but no IP addresses are assigned.

I tried to fool around in the Control Panel of the Synology to enable a DHCP server for the second LAN interface. Eventually I got an IP on the desktop and I could load the Synology server, but I couldn't access the internet.

After some DuckDuckGo-ing and wading through all the posts saying that this is a bad idea and it's not how the Synology should be used, I found a Github repo that said it can bridge the two networks of a Synology. The script there is a bit of an overkill for what I needed, so here is the gist that I needed to get things working:

First, enable vSwitch:

Bridging networks with a Synology NAS
Where to find the vSwitch Settings

Then SSH into the NAS and run the following two commands:

> sudo ovs-vsctl del-br ovs_eth1
> sudo ovs-vsctl add-port ovs_eth0 eth1
> sudo ovs-vsctl show 
    Bridge "ovs_eth0"
        Port "eth1"
            Interface "eth1"
        Port "eth0"
            Interface "eth0"
        Port "ovs_eth0"
            Interface "ovs_eth0"
                type: internal

If the output of the final command shows a bridge and two associated Ports, you're good to go and browse the Internet!

I don't actually intend to keep this as a long term solution. A NAS is not meant to function as a router or switch, so it's not optimized for this. A real switch is probably faster, but a Speedtest shows that I have 250 Mb/s download, so it's pretty good for now, until I get around to buying a switch.

I’m publishing this as part of 100 Days To Offload - Day 13.

]]>