mrnd's recent activity

  1. Comment on What are your favorite CLI tools/applications? in ~comp

    mrnd
    Link
    For me, excluding the standard *nix tools, it is fzf. At its base, it's a simple filter: you can pipe lines to it and interactively and fuzzily select out of them. But it also provides a lot of...

    For me, excluding the standard *nix tools, it is fzf.

    At its base, it's a simple filter: you can pipe lines to it and interactively and fuzzily select out of them. But it also provides a lot of integrations out of the box like shell commands (history search, selecting files, entering directories and so on) and editor integrations. And then there are of course a lot of third-party integrations, of which my favorite is probably notational-fzf-vim for note taking and searching.

    It's just so simple tool, that advances the whole ecosystem by providing a simple way to greatly improve upon the standard CLI user experience.

    3 votes
  2. Comment on What's a widely criticized thing that you feel is worth defending? in ~talk

    mrnd
    Link Parent
    I think this is somewhat universal want for humans. It's nice to tell and hear stories about shared cultural heroes. This was how oral folk tales worked, and it's why today we like big franchises....

    I don't think there's any reason why stories such as these need to be expanded endlessly. Unless of course one is a shareholder of the IP.

    I think this is somewhat universal want for humans. It's nice to tell and hear stories about shared cultural heroes. This was how oral folk tales worked, and it's why today we like big franchises. Familiarity is good.

    (This is incidentally why I'd like copyright much weakened: ideal human culture freely uses established characters from the shared mythology.)

    2 votes
  3. Comment on What's a widely criticized thing that you feel is worth defending? in ~talk

    mrnd
    Link
    This is something more trivial... I think the Tolkien-fandom unfairly dismisses the story of Shadow of Mordor games. ...This probably requires some backgroud for those who have no idea what I...

    This is something more trivial...

    I think the Tolkien-fandom unfairly dismisses the story of Shadow of Mordor games.

    ...This probably requires some backgroud for those who have no idea what I mean.

    Shadow of Mordor (2014) and it's sequel Shadow of War (2017) were video games in the Lord of the Rings universe. Their gameplay, especially the fairly innovative Nemesis-system, was mostly praised.

    Their story however took some heavy artistic freedom regarding the lore of the universe. This was deemed especially offending, because in their early marketing they played the "faithful to the lore" card pretty heavily.

    The criticism by the Tolkien community was very wide: from very specific details, like including characters that make the game not fit at literally any point in the timeline, to more abstract problems like messing up the metaphysics of the human soul. And worst of all, the game included its own story, not based on anything Tolkien wrote!

    The sequel was even worse. It introduced many new interpretations of historical characters, like human-formed Shelob, and revealing that the Nazgul are various historical characters. And numerous other things.

    The point being, there was a lot wrong with the games.

    However, these are mostly things I personally care about very little. And it's not like I am generally easy to please, I think the Peter Jackson's movies were quite offending to the books. But they got more, and worse things wrong than these games. Things like characters, themes, aesthetics.

    On aesthetics department, these games follow the movies (mostly at least), but because it has its own story, it doesn't really mess up anything I actually care about. It uses some important characters, yes, but it doesn't ruin them like the movies do. At some points, the interpretations are even inspired.

    Something the games are, is one of the only high budget creations that explore some of my favorite things in Tolkien's world: Eregion, the creation of rings, and the character of Sauron. And using these set pieces on the background, it creates a rather unique version of the world.

    By the end of Shadow of War, the series has created a very interesting story, with extremely inspired ideas. They may be not strictly canon-compliant ideas, but they are still inspired by it, even if some fans disagree. The questions about the Tolkien-world it deals with are relevant even outside the game's version of canon.

    Spoilery details about what I mean

    Probably the main idea I love is the transferring of the Rings. Multiple times the game moves one of the Nine rings from person to person, and this is something that completely works with established lore.

    Also, sexy Shelob makes perfect sense, thank you very much. And not everything needs to be literal anyway.

    Also, Talion is not actully brought back to life, but ratjer is prevented from dying, something we already know does happen.

    One of the most convincing criticism about the first game was how it glorified violence. The second game however makes it very clear that the game understands the risk of power, and supports Tolkien's recurring theme of its corruptive influence.

    TL;DR: The Tolkien community dismisses the games as fanfiction, but fanfiction is good actually.

    12 votes
  4. Comment on Sami are the only officially recognised indigenous people in the EU and some of their languages are on the brink of extinction in ~humanities

    mrnd
    (edited )
    Link Parent
    This is (at least not always) the relevant part of the definition. For example, UN defines indigenous people like this: I'd say that "non-dominant" is the imprtant part. Practically this often...

    This is (at least not always) the relevant part of the definition. For example, UN defines indigenous people like this:

    "Indigenous communities, peoples, and nations are those that, having a historical continuity with pre-invasion and pre-colonial societies that developed on their territories, consider themselves distinct from other sectors of the societies now prevailing in those territories, or parts of them. They form at present non-dominant sectors of society and are determined to preserve, develop, and transmit to future generations their ancestral territories, and their ethnic identity, as the basis of their continued existence as peoples, in accordance with their own cultural patterns, social institutions and legal systems.

    I'd say that "non-dominant" is the imprtant part. Practically this often means that they don't have a state.

    This is important for example in Finland, where neither Sami or Finnic tribes were the earliest humans there ever, and have inhabited area of Finland for approximately the same time. However, later on Finns colonized them and became the dominant culture, while the Sami were left without state, as guests/subjects in the different Scandinavian states.

    4 votes
  5. Comment on Doctor Who S12E03 'Orphan 55' in ~tv

    mrnd
    Link
    I think this is fairly easy to accept, as we have seen her wiping and transmitting memories before. But besides that. Yeah, I'm having similar feelings. Chibnall's style seems to be portraying...

    I forgot to ask this last week: when did the Doctor become a touch-telepath? Suddenly, she has the power to read minds and even influence people. This power hasn't surfaced before.

    I think this is fairly easy to accept, as we have seen her wiping and transmitting memories before.

    But besides that. Yeah, I'm having similar feelings.

    Chibnall's style seems to be portraying very lukewarm takes as very radical (racism bad, climate change bad [but amazon good]). And he doesn't really say anything about them beyond that. I'm getting fairly annoyed by that.

    Doctor Who should be political, but this is not good way to do that.

    3 votes
  6. Comment on What are you using these days as an alternative to YouTube? in ~tech

    mrnd
    Link Parent
    Along the lines of not-actually-a-replacement: RSS. Just subscribe to Youtube channels with your favorite RSS reader. Rest of the internet (including Tildes) seems to be able to handle the...

    Along the lines of not-actually-a-replacement: RSS.

    Just subscribe to Youtube channels with your favorite RSS reader. Rest of the internet (including Tildes) seems to be able to handle the discovery of new channels just fine.

    2 votes
  7. Comment on What are some good idle/incremental games? in ~games

    mrnd
    Link
    Factory Idle is something I just found, but it seems fun. Much more involved than paperclips though.

    Factory Idle is something I just found, but it seems fun. Much more involved than paperclips though.

  8. Comment on Finland is considering a four-day week. Is this the secret of happiness? in ~life

  9. Comment on What are you looking for in a private messaging app? in ~comp

    mrnd
    (edited )
    Link Parent
    Their main draw (federation) is a thing pretty much only tech people care about. However, it seems that at least Mastodon has other fairly practical property: people who are banned or driven out...

    Their main draw (federation) is a thing pretty much only tech people care about.

    However, it seems that at least Mastodon has other fairly practical property: people who are banned or driven out of Twitter can use it, on their own terms. This is of course a consequence of federation, but other federated systems haven't really been able to market that aspect of it effectively.

    11 votes
  10. Comment on Star Wars: The Rise of Skywalker - Discussion Thread in ~movies

    mrnd
    (edited )
    Link Parent
    That's fair! But to me personally, TLJ was a really good movie, and if a smart director had continued on the path laid by it, we could have had a great trilogy that justified itself.

    That's fair!

    But to me personally, TLJ was a really good movie, and if a smart director had continued on the path laid by it, we could have had a great trilogy that justified itself.

    4 votes
  11. Comment on Star Wars: The Rise of Skywalker - Discussion Thread in ~movies

    mrnd
    (edited )
    Link
    Feeling very conflicted. There were some fun parts, and some things I really liked, like Rey's and Ben's relationship. Some aesthetic choises were really cool: I always like more occult and dark...

    Feeling very conflicted. There were some fun parts, and some things I really liked, like Rey's and Ben's relationship. Some aesthetic choises were really cool: I always like more occult and dark portrayal of Sith things. Some things I really hated (rey's parentage, that had zero reason to happen).

    Overall, it felt like a collection of things, with absolutely no coherence. I disliked how it betrayed all the Last Jedi's themes, to the point that it needed to "explain" away all the generic nitpicky internet criticisms of the previous movie (luke's "i was wrong", "holdo maneuver", snoke, rey's parentage again). The Last Jedi dealt with some really interesting ideas around Jedi and Sith and how the mistakes of previous generations might be avoided, but this one refused to engage with any of that, and simply had an another ending of the Jedi winning over the Sith: the fundamental conflicts as presented by Last Jedi were left unresolved.

    It lacked confidence as the final film of the series, and was full of generic stuff that wasn't taken to any real conclusions.

    If I'm allowed a "hot take", bringing back J. J. Abrams was definitely a mistake: he had no idea what to do with the ending.

    20 votes
  12. Comment on Finland's finance minister deleted an Instagram post and issued an apology after criticism by a human rights group in ~news

    mrnd
    (edited )
    Link Parent
    Yes, this is it pretty much it, they should be allowed to return. Our populist voices are simply shouting "security risk" very loudly. And yes, to many just ignoring the situation feels like the...

    but have these women been convicted of or even charged with any crimes? Why should they not be allowed to return to their home country (and processed by the criminal justice system, if necessary) if the authority holding them wants them gone from their territory?

    Yes, this is it pretty much it, they should be allowed to return. Our populist voices are simply shouting "security risk" very loudly.

    Is it simply that Finland's government thinks these people are evil but can't prove criminal wrong-doing, so it's trying to effectively prosecute these people outside of the criminal justice system because they can?

    And yes, to many just ignoring the situation feels like the easiest way out. But the government seems to be actively seeking ways to get them home, it's the opposition which loudly opposes this. This minister in question is going against the government line, for the reasons I tried to list in my sibling comment.

    5 votes
  13. Comment on Finland's finance minister deleted an Instagram post and issued an apology after criticism by a human rights group in ~news

    mrnd
    Link Parent
    Yes, the women in question are Finnish citizens who joined ISIS. So they have the right to return, as they haven't been prosecuted in any way. But also people are very wary of allowing it, because...

    Yes, the women in question are Finnish citizens who joined ISIS. So they have the right to return, as they haven't been prosecuted in any way. But also people are very wary of allowing it, because of well, ISIS.

    4 votes
  14. Comment on Finland's finance minister deleted an Instagram post and issued an apology after criticism by a human rights group in ~news

    mrnd
    Link
    This has been a fairly complex discussion in Finland these recent days. Certain media outlets have been on a campaign to expose our new government's "secret and illegal" plan of transporting...

    This has been a fairly complex discussion in Finland these recent days. Certain media outlets have been on a campaign to expose our new government's "secret and illegal" plan of transporting willing finnish ISIS-captives home. There's multiple issues people are talking about: whether the foreign ministry has followed the procedures correctly, have they communicated enough and some questioning if bringing terrorists home should even be on the table, even if it's their right as citizens. Many vocal voices are making the distinction berween bringing only the children home, and bringing both the children and their mothers. But like the article says, separating the children is not really even an option, so this poll by the Centre party's minister seems in very poor taste.

    Recently the leader of our most right-wing populist party, the Finns, implied that recent airport excersise by our customs agency Tulli was in fact a cover operation to bring the captives here. This seemed like a obvious attempt at creating distrust, and wasn't received very well by their opponents. Their base seems to like that kind of thing however.

    The Centre party is in somewhat uncomfortable coalition with more left-leaning parties, and they seem to be constantly polling their base on whether it's time to abandon the current coalition and attempt a place in a new right-wing government with the Finns and the National Coalition. For them, seeing the opposition parties getting political points with this kind of populist posturing is very worrisome.

    And that anxiety seems to be what caused this particular episode.

    3 votes
  15. Comment on Day 7: Amplification Circuit in ~comp

    mrnd
    Link
    This time the difference between parts was very large: part 1 was a breeze, but part 2 required much more. My original Intcode accepted only a static list as input, and outputted similar list. And...

    This time the difference between parts was very large: part 1 was a breeze, but part 2 required much more.

    My original Intcode accepted only a static list as input, and outputted similar list. And because everything is immutable in Elixir, linking the amplifiers together seemed very hard at first...

    One possibility was to take snapshots of memory and pointer position, and change inputs in-between. However, in the end I finally got to really benefit from Elixir: I ran each amplifier in a separate process, and added ability to pass inputs and outputs through messages. Now I can do interactive IO, but the old tests from earlier days pass too!

    The complete Intcode module
    defmodule Intcode do
      @moduledoc """
      Intcode interpreter.
    
      ## Position and immediate mode
    
      Two modes for instruction arguments are available. Position mode reads from the address specified, immediate mode uses the value itself. From right-to-left, two digits are for opcode, and the rest of the digits define mode for argument n.
    
      - Position mode opcode:  00001 / 1
      - Immediate mode opcode: 11101 (as many flags as there are parameters)
    
      ## Implemented instructions
    
      operator        | opcode | params
      --------------- | ------ | --------------
      add             | 1      | a, b, result
      multiply        | 2      | a, b, result
      read input      | 3      | write address
      write output    | 4      | read address
      jump-if-true    | 5      | bool, pointer
      jump-if-false   | 6      | bool, pointer
      less than       | 7      | a, b, result
      equals          | 8      | a, b, result
      halt            | 99     |
    
      """
    
      @doc """
      Run a program that reads process messages as input. 
    
      Requires `:intcode_pid` message with PID to write output to.
    
      If `result_pid` is given, sends the VM state to that process when finished.
    
      message      | direction  | value
      ------------ | ---------- | -----------------------------------------------
      :intcode_out | OUT        | VM state 
      :intcode_pid | IN         | PID to send output to when using "write output"
      :intcode_msg | OUT        | Message when using "write output"
    
      """
      def run_process(program, result_pid) do
        spawn(fn ->
          output_pid =
            receive do
              {:intcode_pid, pid} -> pid
            end
    
          result = execute(0, program, channel: output_pid, out: [])
    
          if is_pid(result_pid) do
            send(result_pid, {:intcode_out, result})
          end
        end)
      end
    
      @doc """
      Run a program with input list and return the output list.
      """
      def run(program, input) do
        {_, io} = execute(0, program, in: input, out: [])
        io[:out]
      end
    
      @doc """
      Run a program and return the value of address 0.
      """
      def run(program) do
        {memory, _} = execute(0, program, [])
        Enum.at(memory, 0)
      end
    
      @doc """
      Run a program with parameters i and j and return the value of address 0.
      """
      def run(program, i, j) do
        memory =
          program
          |> List.replace_at(1, i)
          |> List.replace_at(2, j)
    
        run(memory)
      end
    
      @doc """
      Execute a given program and return memory.
      """
      def execute(program) do
        {memory, _} = execute(0, program, [])
        memory
      end
    
      @doc """
      Execute a given program and return memory and output.
      """
      def execute(program, input) do
        execute(0, program, in: input, out: [])
      end
    
      @doc """
      Print out memory.
      """
      def memdump(memory) do
        memory
        |> Enum.map(&Integer.to_string/1)
        |> Enum.reduce("", &(&2 <> "," <> &1))
        |> String.slice(1..-1)
      end
    
      @doc """
      Loads a comma separated program from a given path.
      """
      def loadinput(file) do
        File.read!(file)
        |> String.trim()
        |> String.split(",")
        |> Enum.map(&String.to_integer/1)
      end
    
      # Private methods
    
      defp execute(pointer, memory, io) do
        if pointer < length(memory) do
          {new_pointer, new_memory, new_io} = parse_instruction(memory, pointer, io)
          execute(new_pointer, new_memory, new_io)
        else
          {memory, io}
        end
      end
    
      defp parse_instruction(memory, pointer, io) do
        digits =
          Enum.at(memory, pointer)
          |> Integer.digits()
          |> Enum.reverse()
    
        opcode_list = digits |> Enum.slice(0, 2)
        opcode = Enum.at(opcode_list, 0) + Enum.at(opcode_list, 1, 0) * 10
    
        modes = digits |> Enum.slice(2..-1)
    
        case opcode do
          # add
          1 ->
            {pointer + 4,
             change(
               memory,
               pointer + 3,
               reference(memory, pointer, modes, 1) +
                 reference(memory, pointer, modes, 2)
             ), io}
    
          # multiply
          2 ->
            {pointer + 4,
             change(
               memory,
               pointer + 3,
               reference(memory, pointer, modes, 1) *
                 reference(memory, pointer, modes, 2)
             ), io}
    
          # read input
          3 ->
            # Read from io[:in] Enumerable, if available,
            # otherwise expect a message
            stdin = Keyword.get(io, :in, nil)
            stdout = Keyword.get(io, :out, nil)
    
            {read_value, new_io} =
              if stdin == nil do
                receive do
                  {:intcode_msg, value} -> {value, io}
                end
              else
                {Enum.at(stdin, 0, -999),
                 [in: Enum.slice(stdin, 1..-1), out: stdout]}
              end
    
            {pointer + 2, change(memory, pointer + 1, read_value), new_io}
    
          # write output
          4 ->
            # Write to io[:out] list, if available,
            # otherwise send a message
            stdin = Keyword.get(io, :in, nil)
            stdout = Keyword.get(io, :out, nil)
            value = reference(memory, pointer, modes, 1)
    
            new_io =
              if stdin == nil do
                send(io[:channel], {:intcode_msg, value})
                [channel: io[:channel], out: [value | stdout]]
              else
                [in: stdin, out: [value | stdout]]
              end
    
            {pointer + 2, memory, new_io}
    
          # jump-if-true
          5 ->
            new_pointer =
              if reference(memory, pointer, modes, 1) != 0 do
                reference(memory, pointer, modes, 2)
              else
                pointer + 3
              end
    
            {new_pointer, memory, io}
    
          # jump-if-false
          6 ->
            new_pointer =
              if reference(memory, pointer, modes, 1) == 0 do
                reference(memory, pointer, modes, 2)
              else
                pointer + 3
              end
    
            {new_pointer, memory, io}
    
          # less than
          7 ->
            a = reference(memory, pointer, modes, 1)
            b = reference(memory, pointer, modes, 2)
    
            value =
              if a < b do
                1
              else
                0
              end
    
            {pointer + 4, change(memory, pointer + 3, value), io}
    
          # equals
          8 ->
            a = reference(memory, pointer, modes, 1)
            b = reference(memory, pointer, modes, 2)
    
            value =
              if a == b do
                1
              else
                0
              end
    
            {pointer + 4, change(memory, pointer + 3, value), io}
    
          # halt
          99 ->
            {length(memory), memory, io}
    
          unknown ->
            raise "Invalid instruction #{unknown}"
        end
      end
    
      defp reference(memory, pointer, modes, n) do
        if Enum.at(modes, n - 1, 0) == 1 do
          Enum.at(memory, pointer + n)
        else
          Enum.at(memory, Enum.at(memory, pointer + n))
        end
      end
    
      defp change(memory, pointer, value) do
        List.replace_at(memory, Enum.at(memory, pointer), value)
      end
    end
    
    Part 1 and part 2
    defmodule Day7 do
      @moduledoc "Amplification Circuit"
    
      def solution_part1(filename) do
        program = Intcode.loadinput(filename)
    
        Enum.map(get_permutations(), fn permutation ->
          run(program, permutation)
        end)
        |> Enum.max()
      end
    
      def solution_part2(filename) do
        program = Intcode.loadinput(filename)
        permutations = permute([5, 6, 7, 8, 9])
    
        Enum.each(permutations, fn permutation ->
          feedback_loop(program, permutation)
        end)
    
        # Receive all results and select maximum value
        Enum.map(0..(length(permutations) - 1), fn _ ->
          receive do
            {:intcode_out, {_, [channel: _, out: out]}} -> hd(out)
          end
        end)
        |> Enum.max()
      end
    
      @doc "Run the program once for every setting. Returns the output of the final program."
      def run(program, phase_settings) do
        Enum.reduce(phase_settings, 0, fn x, prev_output ->
          out = Intcode.run(program, [x, prev_output])
          hd(out)
        end)
      end
    
      @doc "Start the program once for every setting, setting up a loop."
      def feedback_loop(program, phase_settings) do
        # Start the amplifiers in seperate processes
        processes =
          Enum.map(0..4, fn index ->
            final = if index == 4, do: self(), else: nil
            Intcode.run_process(program, final)
          end)
    
        Enum.each(0..4, fn index ->
          process = Enum.at(processes, index)
          next = Enum.at(processes, index + 1, Enum.at(processes, 0))
    
          # Send the PID of the next amplifier in line
          send(process, {:intcode_pid, next})
    
          # Send the phase setting
          send(process, {:intcode_msg, Enum.at(phase_settings, index)})
        end)
    
        # Send initial input to the first amplifier
        send(hd(processes), {:intcode_msg, 0})
      end
    
      defp get_permutations do
        permute([0, 1, 2, 3, 4])
      end
    
      defp permute([]), do: [[]]
    
      defp permute(list) do
        for x <- list, y <- permute(list -- [x]), do: [x | y]
      end
    end
    

    Oh, and the permutation function is from Rosetta Code.

    2 votes
  16. Comment on Day 6: Universal Orbit Map in ~comp

    mrnd
    Link
    This time the problem was fairly easy to understand, but I struggled surprisingly much this this one. I probably chose a bad data structure, and unfamiliarity with functional approach to this kind...

    This time the problem was fairly easy to understand, but I struggled surprisingly much this this one. I probably chose a bad data structure, and unfamiliarity with functional approach to this kind of problem didn't exactly help.

    Especially the solution to first part feels really unclean: I basically create a very complex recursive list, and then just... flatten and sum it.

    I actually like my solution to the second part though, it feels kind of elegant.

    Part 1 and few common functions
      def solution_part1(filename) do
        loadinput(filename)
        |> parse_orbits
        |> start_walk
        |> List.flatten()
        |> Enum.sum()
      end
    
      defp loadinput(filename) do
        File.read!(filename)
        |> String.split("\n")
        |> Enum.map(&String.split(&1, "\)"))
      end
    
      # Part 1
    
      # Map objects to their orbiters
      defp parse_orbits(orbit_list) do
        map = %{}
    
        Enum.reduce(orbit_list, map, fn orbit, map_acc ->
          insert_orbit_to_map(map_acc, orbit)
        end)
      end
    
      defp insert_orbit_to_map(map, orbit) do
        object = Enum.at(orbit, 0)
        orbiter = Enum.at(orbit, 1)
        # Add orbiter to the list of object's orbiters,
        # and create new list if it's the first
        Map.put(map, object, [orbiter | Map.get(map, object, [])])
      end
    
      defp start_walk(map) do
        walk_and_count(map["COM"], map, 0)
      end
    
      defp walk_and_count(orbiters, _, _depth)
           when orbiters == [] do
        []
      end
    
      # For every orbiting object, mark its depth, and recurse deeper
      defp walk_and_count(orbiters, map, depth) do
        Enum.map(orbiters, fn orbiter ->
          orbiters_of_orbiter = Map.get(map, orbiter, [])
          depth = depth + 1
    
          [depth, walk_and_count(orbiters_of_orbiter, map, depth)]
        end)
      end
    
    Part 2
      def solution_part2(filename) do
        loadinput(filename)
        |> find_santa()
      end
    
      # Map every orbiting object to its parent
      defp parse_reverse_orbits(orbit_list) do
        map = %{}
    
        Enum.reduce(orbit_list, map, fn orbit, acc ->
          key = Enum.at(orbit, 1)
          value = Enum.at(orbit, 0)
          Map.put(acc, key, value)
        end)
      end
    
      defp find_santa(list) do
        map_rise = parse_reverse_orbits(list)
    
        # For both objects, find the path to COM.
        santa = path_upwards(map_rise, "SAN", [])
        you = path_upwards(map_rise, "YOU", [])
    
        weave_paths(santa, you)
      end
    
      defp path_upwards(_, current, path)
           when current == nil do
        path
      end
    
      defp path_upwards(map_rise, current, path) do
        com = Map.get(map_rise, current, nil)
        path_upwards(map_rise, com, [current | path])
      end
    
      defp weave_paths(santa, you) do
        # Drop the parts that are common to both,
        # to find the unique parts
        if hd(santa) == hd(you) do
          weave_paths(tl(santa), tl(you))
        else
          # Finally, sum the lengths of the unique parts
          length(you) + length(santa) - 2
        end
      end
    
    2 votes
  17. Comment on Day 5: Sunny with a Chance of Asteroids in ~comp

    mrnd
    Link Parent
    Also, because I like wasting time, I later added an additional detail: if opcode 3 (input read), can't find input, it produces error code -999. With this, I created the following intcode program:...

    Also, because I like wasting time, I later added an additional detail: if opcode 3 (input read), can't find input, it produces error code -999. With this, I created the following intcode program:

    "3,17,1008,17,-999,19,1005,19,20,1,17,18,18,1105,1,0,0,0,0,0,4,18"

    Given a list of numbers as input, it loops through them and sums them together!

    3 votes
  18. Comment on Day 5: Sunny with a Chance of Asteroids in ~comp

    mrnd
    Link
    And my adventure with Elixir continues! My architecture from day 2 held up, and adding new opcodes was straightforward. The hardest part was parsing the opcode and mode arguments, but in the end I...

    And my adventure with Elixir continues!

    My architecture from day 2 held up, and adding new opcodes was straightforward. The hardest part was parsing the opcode and mode arguments, but in the end I found a rather simple solution.

    Intcode module
    defmodule Intcode do
      @moduledoc "Intcode interpreter."
    
      @doc """
      Run a program with input and return the output.
      """
      def run(program, input) do
        {_, io} = execute(0, program, in: input, out: [])
        out = io[:out]
        out
      end
    
      @doc """
      Loads a comma separated program from a given path.
      """
      def loadinput(file) do
        File.read!(file)
        |> String.trim()
        |> String.split(",")
        |> Enum.map(&String.to_integer/1)
      end
    
      # Private methods
    
      defp execute(pointer, memory, io) do
        if pointer < length(memory) do
          {new_pointer, new_memory, new_io} = parse_instruction(memory, pointer, io)
          execute(new_pointer, new_memory, new_io)
        else
          {memory, io}
        end
      end
    
      defp parse_instruction(memory, pointer, io) do
        digits =
          Enum.at(memory, pointer)
          |> Integer.digits()
          |> Enum.reverse()
    
        opcode_list = digits |> Enum.slice(0, 2)
        opcode = Enum.at(opcode_list, 0) + Enum.at(opcode_list, 1, 0) * 10
    
        modes = digits |> Enum.slice(2..-1)
    
        case opcode do
          # add
          1 ->
            {pointer + 4,
             change(
               memory,
               pointer + 3,
               reference(memory, pointer + 1, Enum.at(modes, 0, 0)) +
                 reference(memory, pointer + 2, Enum.at(modes, 1, 0))
             ), io}
    
          # multiply
          2 ->
            {pointer + 4,
             change(
               memory,
               pointer + 3,
               reference(memory, pointer + 1, Enum.at(modes, 0, 0)) *
                 reference(memory, pointer + 2, Enum.at(modes, 1, 0))
             ), io}
    
          # read input
          3 ->
            stdin = io[:in]
            stdout = io[:out]
    
            {pointer + 2, change(memory, pointer + 1, hd(stdin)), [in: tl(stdin), out: stdout]}
    
          # write output
          4 ->
            stdin = io[:in]
            stdout = io[:out]
    
            {pointer + 2, memory,
             [in: stdin, out: [reference(memory, pointer + 1, Enum.at(modes, 0, 0)) | stdout]]}
    
          # jump-if-true
          5 ->
            new_pointer =
              if reference(memory, pointer + 1, Enum.at(modes, 0, 0)) != 0 do
                reference(memory, pointer + 2, Enum.at(modes, 1, 0))
              else
                pointer + 3
              end
    
            {new_pointer, memory, io}
    
          # jump-if-false
          6 ->
            new_pointer =
              if reference(memory, pointer + 1, Enum.at(modes, 0, 0)) == 0 do
                reference(memory, pointer + 2, Enum.at(modes, 1, 0))
              else
                pointer + 3
              end
    
            {new_pointer, memory, io}
    
          # less than
          7 ->
            a = reference(memory, pointer + 1, Enum.at(modes, 0, 0))
            b = reference(memory, pointer + 2, Enum.at(modes, 1, 0))
    
            value =
              if a < b do
                1
              else
                0
              end
    
            {pointer + 4, change(memory, pointer + 3, value), io}
    
          # equals
          8 ->
            a = reference(memory, pointer + 1, Enum.at(modes, 0, 0))
            b = reference(memory, pointer + 2, Enum.at(modes, 1, 0))
    
            value =
              if a == b do
                1
              else
                0
              end
    
            {pointer + 4, change(memory, pointer + 3, value), io}
    
          # halt
          99 ->
            {length(memory), memory, io}
    
          _ ->
            raise "Invalid instruction!"
        end
      end
    
      defp reference(memory, pointer, immediate_mode) do
        if immediate_mode == 1 do
          Enum.at(memory, pointer)
        else
          Enum.at(memory, Enum.at(memory, pointer))
        end
      end
    
      defp change(memory, pointer, value) do
        List.replace_at(memory, Enum.at(memory, pointer), value)
      end
    end
    
    Part 1
        input = Intcode.loadinput(filename)
        hd(Intcode.run(input, [1]))
    
    Part 2
        input = Intcode.loadinput(filename)
        hd(Intcode.run(input, [5]))
    
    4 votes
  19. Comment on Day 3: Crossed Wires in ~comp

    mrnd
    Link
    So, this year's event is my first experience with Elixir, so this is very likely not that elixiry. Even just glancing over this now, I saw a few obvious improvements. But it works, so. Both...

    So, this year's event is my first experience with Elixir, so this is very likely not that elixiry. Even just glancing over this now, I saw a few obvious improvements. But it works, so.

    Both solutions are in the same file and use common functions, so I won't try to split them.

    Part 1 and Part 2
    defmodule Day3 do
      def solution_part1(filename) do
        get_wire_intersections(filename)
        |> Enum.map(fn {a, _} -> a end)
        |> Enum.min_by(&manhattan/1)
        |> manhattan
      end
    
      def solution_part2(filename) do
        get_wire_intersections(filename)
        |> Enum.min_by(&combined_steps/1)
        |> combined_steps
      end
    
      defp get_wire_intersections(filename) do
        wire1 =
          parse_input(filename, 0)
          |> navigate()
    
        wire2 =
          parse_input(filename, 1)
          |> navigate()
    
        intersection_recurser(wire1, wire2, []) |> tl
      end
    
      defp navigate(route) do
        Enum.reduce(route, [{0, 0, 0}], fn command, acc_route ->
          move(acc_route, command)
        end)
        |> List.keysort(1)
        |> List.keysort(0)
      end
    
      defp move(route, {direction, movement}) do
        case direction do
          "U" ->
            step(route, movement, 0, 1)
    
          "D" ->
            step(route, movement, 0, -1)
    
          "L" ->
            step(route, movement, -1, 0)
    
          "R" ->
            step(route, movement, 1, 0)
        end
      end
    
      defp parse_input(filename, n) do
        File.read!(filename)
        |> String.trim()
        |> String.split("\n")
        |> Enum.at(n)
        |> String.split(",")
        |> Enum.map(
          &{String.first(&1),
           String.slice(&1, 1..-1)
           |> String.to_integer()}
        )
      end
    
      defp intersection_recurser(wire1, wire2, result)
           when wire1 == [] or wire2 == [] do
        result
      end
    
      defp intersection_recurser(wire1, wire2, result) do
        case compare_duple(hd(wire1), hd(wire2)) do
          :equal ->
            intersection_recurser(
              tl(wire1),
              tl(wire2),
              [{hd(wire1), hd(wire2)} | result]
            )
    
          :smaller ->
            intersection_recurser(tl(wire1), wire2, result)
    
          :greater ->
            intersection_recurser(wire1, tl(wire2), result)
        end
      end
    
      defp compare_duple({x1, x2, _}, {y1, y2, _}) do
        if x1 == y1 do
          if x2 == y2 do
            :equal
          else
            if x2 < y2 do
              :smaller
            else
              :greater
            end
          end
        else
          if x1 < y1 do
            :smaller
          else
            :greater
          end
        end
      end
    
      defp step(route, steps, x, y) do
        Enum.reduce(1..steps, route, fn _, route_new ->
          {last_x, last_y, count} = List.first(route_new)
          {new_x, new_y} = {last_x + x, last_y + y}
          [{new_x, new_y, count + 1} | route_new]
        end)
      end
    
      defp manhattan({x, y, _}) do
        abs(x) + abs(y)
      end
    
      defp combined_steps({{_, _, steps1}, {_, _, steps2}}) do
        steps1 + steps2
      end
    end
    
    3 votes
  20. Comment on The homeownership obsession - How buying homes became a part of the American dream—and also a nightmare in ~life

    mrnd
    (edited )
    Link Parent
    Economically speaking in ideal market, rent should be equal to (mortgage interest) + (maintenance) + (other fees). So rent is not really wasted in general sense. In many countries goverments do...

    Economically speaking in ideal market, rent should be equal to (mortgage interest) + (maintenance) + (other fees). So rent is not really wasted in general sense. In many countries goverments do support homeowning in various ways, which can make ownership cheaper. But that is very time and place specific, not a general rule.

    These calculations need to be made when one makes the decisions for themselves, but it's doesn't really mean anything to say that rent is automatically lost money.

    4 votes