Overcomplicated Multireplace

I found a niche usage of the multireplace function using math:
@{(round((var1 - 50)/(x))+1) This is sample text the first.|I am the second sample text.|Behold, a third sample text.}

It takes var1 as an integer and does funky stuff until it ends up being 1, 2, or 3. The variable x affects the threshold of when it will become one of the readable numbers.

If x = 50, var1 being below 25 triggers the first sample text and if var1 is at least 75, it triggers the third sample text. Anything in between triggers the second sample text.

The variable x changes the trigger numbers by at a rate of 1:0.5 from the base number 50. So if x = 30, the trigger numbers will be 35 and 65. (You still shouldnā€™t divide by 0.)

This is a very complicated version of using the different ifā€™s and elseā€™s:

*if var1 < (50-(x/2))
    This is a sample text the first.
*elseif var1 >= (50+(x/2))
     Behold, a third sample text.
*else
    I am the second sample text.

I would not recommend using this alternative. No, really. I just thought it was neat.

15 Likes

Definitely neat, but considering I scored a 17 in the Math section on my first ACT test a few years back, I think Iā€™ll stick to the normal conventions here. :joy:

3 Likes

The idea is pretty neat, but I think the logic is a little funky.

This is not true, (round((var1 - 50)/(x))+1) can still end up being 0, which will cause an error in the multireplace.

For example, if x = 50 and var1 = 24, then:

  • var1 - 50 = 24 - 50 = - 26
  • (- 26) / 50 = - 0.52
  • round(- 0.52) = - 1
  • (- 1) + 1 = 0
  • var1 - 50 = 75 - 50 = 25
  • 25 / 50 = 0.5
  • round( 0.5 ) = 1
  • 1 + 1 = 2

This will trigger the second option.

You either didnā€™t notice that multireplace indices start at 1 and not 0, different than an array, or you forgot to check the lower limits for each condition.

For now, I think you can fix it (I could be wrong, itā€™s 3 a.m. here :sweat_smile::sleeping:) if you use 2 instead of 1

@{(round( (var - 50) / 50 ) + 2 ) One|Two|Three}

But I donā€™t think itā€™ll work for all divisors. This is an interesting challenge, though. Iā€™ll try to work a solution as well :stuck_out_tongue_winking_eye:.

1 Like

Nice. This sort of construct should really get more explicit syntax, though. Coding all the logic inside a single parentheses pair is prone to error, as @cup_half_empty pointed out.

Iā€™ve wished CS had some proper select case statements, as well; multireplace somewhat takes the place of them, but not entirely, and not across multiple lines.

1 Like

I have a halfass solution for this, but itā€™s not so cool as @StorybookParagon and itā€™s still liable to break.

@{ (round( ((x * n) / 100) + 0.499 )) One | Two | ā€¦ | Umpteen}

Where:

  • x is the input variable (with a value between 0.1 and 100), and
  • n is the number of elements in the multireplace (starting at 2).

Note that:

  • if x < 0.1 the result will be zero! This will break the code. So x should be at least 0.1;
  • if x > 100, that is, 100.1, the return will be (n + 1), which will also break the code;
  • all options get the same ā€œbandwidthā€, meaning that there is no ration like in @StorybookParagonā€™s solution. If you have 3 options inside the multireplace, the points of inflection will be at every (100 / 3), that is, at every 33.33ā€¦. In other words, from 0 to 33.33ā€¦ multireplace will return the first option, from 33.4 to 66.66ā€¦, the second and from 66.7 to 100, the third.

This should work no matter how many elements you have inside the multireplace.

Edit:

  • if x < 0.1 the result will be zero! This will break the code. So x should be at least 0.1;
  • if x > 100, that is, 100.1, the return will be (n + 1), which will also break the code;

As I wrote this I realized that you can circunvent this problem if you use this:

@{ (round( ((x * n) / 100) + 0.5 )) One | Two | ā€¦ | Umpteen | Umpteen }

Note that the last option in the multireplace must be duplicated!

Try writing a program in Java! :joy:

3 Likes

Itā€™s a loving homage to how complicated attempting to cram everything to take up the least space. :joy:

Thanks for showing how you would do it.

1 Like

Hmmm. Good point. Maybe itā€™s possible?

I mean, people woking in the cs_libs invented workarounds for more complicated things beforeā€¦

Sure, itā€™s possible if youā€™re going to edit the language. You can do anything at that point.

cs_lib is explicitly working within the existing syntax of CS, though, which means a new language feature like select case would be outside their scope.

Iā€™m curious now, whatā€™s so special about switch-case that you canā€™t just utilize if-elseifs-else?

It would? I mean, it isnā€™t using the very resources of a system to implement a work/solution within the very system the essence of a classic deterministic finite automata?

That is the magic of refined logic gates like switch, do whiles and for!

They give you the ability to implement game mechanics in a structured functional way. Making the code easyer to write, read and debug than using an assortment of ifs elses gotos and whatnot.

1 Like

Intersting. Apart from some performance calculations and edge cases where you might need to repeat some lines of code in one but not the other, Iā€™ve never seen switchā€™s and if-elseā€™s readability and debuggability as any different. :thinking:

1 Like

Well, sure, the same way you can create various types of loops using *ifs and *gotos. Just like multireplace didnā€™t add any new functionality, neither would this. They both merely let authors write much more compact and readable code in a very common use case: checking a single variable against several different conditions. It also avoids the need to fill a temporary value to simplify writing an if-then-else block.

But also, every block in an if-then-else set is mutually exclusive; you canā€™t write a set in which some but not others may fire, which is common in switch statements. Your only way to achieve that currently is to write every condition as a standalone *if statement. Iā€™d imagine, to use existing CS syntax, authors not using ICF would need to end their case blocks with a flow-control command like *goto, while those using ICF would have fallthrough.

Or if they donā€™t want fallthrough, the language could instead implement a *continue command.

Proposed syntax and example:
*switch ((populace_hate_love %+ social_engineering) + charisma)
   *case 0
      You're a nobody.
   *case < 15
      You're relatively unknown.
   *case < 50
      You have some local renown.
      *set fame %+ 10
      *continue
   *case < 30
      But not much.
   *case < 70
      You are famous statewide.
      *set fame %+ 20
   *case >= 70
      You are recognized nationwide.
      *set fame %+ 30
      *continue
   *case >= 85
      Children write essays about wanting to be you when they grow up.
      *set fame %+ 25
      *continue
   *case > 100
      The national epic is composed in your honor.
      *achieve epic
Implementing that in if-then-else requires a temp value, repeated use of the same variable name, and multiply nested ifs:
*temp scratch_value ((populace_hate_love %+ social_engineering) + charisma)
*if scratch_value = 0
   You're a nobody.
*elseif scratch_value < 15
   You're relatively unknown.
*elseif scratch_value < 50
   You have some local renown.
   *set fame %+ 10
   *if scratch_value < 30
      But not much.
*elseif scratch_value < 70
   You are famous statewide.
   *set fame %+ 20
*else
   You are recognized nationwide.
   *set fame %+ 30
   *if scratch_value >= 85
      Children write essays about wanting to be you when they grow up.
      *set fame %+ 25
      *if scratch_value > 100
         The national epic is composed in your honor.
         *achieve epic

Iā€™m not sure what youā€™re saying here.

The goal of cs_lib is to create complex structures using existing Choicescript syntax, not to alter the language itself by adding new commands.

1 Like

I know, the notion of automata is more a math thing than a computer thing.

But back at the 50s they used Chomsky hierarchy of language types to build a arrangement of these mechanisms in order to code the first FORTRAN compilers.

In choicescript, I believe you can use a similar approach to create simple word parsers. In fact, IRRC, @Twiger_Fluff and @nocturno developed just that for their data structure emulation functions.

I believe the key word here is an set parse automata able to ā€œbuildā€ logical gates based on a string of intructions. After all, parsers are the base of modern code interpreter.

Coding a parser in choicescript is not impossible. But is not simple either. I remember reading about something named ā€œVienna Methodā€ in a math book from the 70s. I will try to find it. Maybe a future where choicescript have proper logical functionality is not just a dreamā€¦

And yes, I just talked about coding a freaking parser subroutine that breaks a string with a switch case written in it as a abstract syntax tree (global var names, values, logical operators, etc), identify the references (everything with an ā€œ*ā€ symbol is a command), execute semantic validation, converts it all in an linear code with jumps and loops then generate a effective procedure based in the string with the case as if the coder had written it with normal choicescript.

As another possibility, CS could also implement PHP-style reversed value checking, with a value in the *switch command which various variable names or expressions on the *case lines will be compared against.

And a potentially more real-world example
*temp companions 0
*switch true
   *case with_legoland
      "You have my bow," Legoland intones.
      *set companions 1

      *continue
   *case with_gilgamesh
      "@{(companions = 0) You have|And} my axe," Gilgamesh burbles.
      *set companions + 1

      *continue
   *case with_borogove
      "@{(companions = 0) You have|And} my sword," Borogove brilligs.
      *set companions + 1

      *continue
   *case with_hobos
      "We're not sure why we're here," the hobos mutter.
      *set companions + 3

      *continue
   *case (communed_with_spirits and (rebuilt_sword and (overthrown_king and ((fame + populace_love) > 100))))
      Green ectoplasm swirls around you swirlingly.
      *set have_spirits true
   *default
      But you are all alone.
1 Like

I must say I in love with your snippets!
The hobos companions!

Thank you for you examples. I really have no idea if I can make my silly idea of a switch case as a callabe subroutine (at least without abusing the use complex first class functions to simulate the behavior of a switch gate like in other languages, in a way that the code doesnā€™t end actually worse than you simply doing it with if and elsifsā€¦).

Man I feel like a idiot for suggesting it butā€¦ It is possible, I think? probably will take the amount of effort of single handle coding Dwarf Fortress thoughā€¦

An choicescript built-in ā€˜high level code interpreterā€™ that simply reads a custom made command (in this case, *case, no pun intended), breaks it in a lower level linear code using an assortment of reference loop trees, reassemble it in pure choicescript them execute the damn thing in front of your eyes!

Probably will work only with global variables thoughā€¦ (uh)