IF system code *wars

Continuing the discussion from Creating a fair comparison:

*fun challenges

I like this idea and would like to try it.

If you’re not familiar with Codewars, Matt summed it up quickly. The idea is to give programming language-neutral coding challenges (for ex: “reverse this string” or “shuffle an array of objects”) and see how people approach implementing them in their favorite language.

While I’m not as interested in the competition aspect, I think it’d be cool to have a bunch of implementation challenges for folks to try out and learn from! Maybe separating out parsers vs choice, but not sure yet.

Here’s my proposed rules:

  1. Fulfill all requirements of the given challenge. Optionally: Fulfill bonus criteria.
  2. You can ask for help or advice, and provide critique/feedback freely. (edited)
  3. Show the raw code in your message, and explain how you fulfilled the challenge requirements (and bonus criteria if applicable). If you can, attach the compiled game file.
  4. You may discuss the merits or limitations of systems and implementations to help yourself and others learn–just be respectful. That means no showboating code or a particular system as “the best” or denigrating it as “the worst”, whether your own code or others. (edited)
  5. No AI!

Here’s some challenges off the top of my head, of varying complexity. The requirements would be elaborated on if people are interested in them! Also feel free to suggest your own.

  1. A rope that can be pulled from place to place (and other rope features).
  2. Place you interact with differently in darkness, low light, and full light.
  3. A bathroom sink (with various sink features).
  4. A combination lock.
  5. A way to input riddle answers.
  6. Conversation system that isn’t a picklist or ask/tell.
  7. Complex NPC with different behaviors and conversation topics based on context.
  8. Disambiguation challenge (for parsers)
  1. Random events that change the surroundings.
  2. Realtime timer.

Suggestions from others:
1.

  1. Display and then transform a picture 360°.
  2. Crossfade between two pictures.
11 Likes

I do think it would be desirable to separate parser and choice challenges as I don’t think the solutions are likely to be directly comparable. Though how you handle hybrid systems I’m not sure. Maybe @jkj_yuio has thoughts.

2 Likes

So more of a parallel text or code chrestomathy like Rosetta Code than a competition? That’d be cool.

IIRC the Twine Cookbook isn’t maintained any more and they’re much simpler snippets, but…

6 Likes

Seems like a fun idea to me – personally I like the idea of keeping all the systems together, it can be interesting to see how a choice system handles a more stereotypically parser-like challenge, and vice versa.

  1. Realtime timer.

My brain at first interpreted this as “runtime error”, which actually could be a funny demonstration challenge (what’s the fastest and/or most amusing way you know to make your system barf?)

… I’m now remembering that I think there was a similar idea on the forums way before my time. Yeah, here’s the first post – you can find them by searching for “coding task.” Might be interesting to see what challenges they used to see if any still seem interesting?

4 Likes

Here’s a choice-based suggestion: a mini Cluedo game with multiple rooms to move through and multiple people also moving around. The choices available on each “turn” are the choices from the current room, plus the choices from whichever person (or people, if you want) are in that room. The winning choice, “accuse [X person] of doing something in [Y room]”, requires a specific person and a specific place.

So for example, if you’re in the Study with Professor Plum, your choices might be “investigate the Study”, “investigate Professor Plum”, “ask Professor Plum to come with me”, “accuse Professor Plum in the Study”. The point is to have the choice list be assembled based on multiple independent factors.

I know this is possible but difficult in Ink using the <- operator; I’m curious how other choice-based systems would approach it.

4 Likes

I don’t really like the idea of these being “challenges” or “wars”, but the idea of seeing how different systems approach a set of canonical problems is interesting and worthwhile.

FWIW, hybrid systems with both choice and parser input need to approach problems so that they can be played by either choice or parser. Sometimes this complicates things a bit more. Or in some approaches alternatively, they can opt for parts of gameplay being just one or the other.

Looking at the suggested list, (6) seems a UI problem and several are complex object problems. There are no graphics related ones, like rotate a picture 360 or cross-fade two pictures or fade a picture in or out. Nor are there any audio or video examples. or techniques benefiting from clicking on words, pictures or UI elements.

So the “challenges”, if you want to call them that, are currently all heavily biased toward text-base parser entry systems.

2 Likes

The ones you suggested are good! I did realize I was biasing toward world model/parser stuff but wasn’t sure if ui stuff would be leaving parsers in the cold entirely. But I can add them. Good to know if these code bits are possible regardless!

Edit: added!

I don’t think this has to be choice-specific honestly! Games like Make It Good and Color The Truth have mechanics like this also, iirc. Even if it’s much more easy in parsers due to the world model, it is probably not trivial to limit actor interactions to rooms?

3 Likes

Was burnt-out from working on my novel, so I decided to give it a try. (To give my brain some rest from writing, mostly.) Some repetitions are unavoidable when doing this with Ink. Not using lists because I don’t like them.

Summary
VAR room = "hallway"
VAR docRoom = "hallway"
VAR sciRoom = "hallway"
VAR butlerRoom = "hallway"
VAR counter = 0

-> hallway

=== hallway
~ room = "hallway"

In the hallway.

<- eachTurn()

 + Examine picture.
    Just a picture.
 
 + Eat statue.
    Tastes crunchy.

 + Enter library.
    -> library

 + Enter kitchen.
    -> kitchen
 
-
-> hallway


=== library
~ room = "library"

In the library.

<- eachTurn()

 + Read book.
    You do that.
 
 + Exit to hallway.
    -> hallway
  

-
-> library



=== kitchen
~ room = "kitchen"

In the kitchen.

<- eachTurn()

 + Examine pot.
    Just a pot.

 
 + Exit to hallway.
    -> hallway
  
-
-> kitchen


=== function schedule(startAtTurn, interval)
    ~ return counter % interval == startAtTurn

=== walk()
{
    - schedule(1, 3):
        ~ move("Doc", docRoom)

    - schedule(2, 3):
        ~ move("butler", butlerRoom)

    - schedule(3, 5):
        ~ move("scientist", sciRoom)
}
-> DONE

=== function pickDestination(loc)
    {
        - loc == "kitchen":
            ~ return "hallway"
            
        - loc == "library":
            ~ return "hallway"
            
        - loc == "hallway":
            ~ return eitherOr("library", "kitchen")
    }
    ~ return "hallway"

=== function move(name, ref personLoc)
    ~ temp from = personLoc
    ~ temp to = pickDestination(personLoc)
    ~ personLoc = to
    {
        - from == to:
            // do nothing.
    
        - room == from:
            The {name} leaves for the {to}.
            
        - room == to:
            The {name} comes in from the {from}.
    }
    
    
    ~ return



=== eachTurn()
    ~ counter++
    <- walk()
    <- see()
    <- people()
    <- accusations()

-> DONE

=== accusations()
    
+ {docRoom == room} Accuse the Doc in the {room}.

    {
    
        - room == "library":
    
        He starts crying: "I did it! I did the thing! Right here, in this very library!"
        
        YOU WON!
        
        -> END
    
    }

    You accuse the Doc. He just laughs smugly: "You got any proof?"
    -> back
        
+ {sciRoom == room} Accuse the scientist in the {room}.
    You accuse the Scientist. She shakes her head in disbelieve.
    -> back
    
+ {butlerRoom == room} Accuse the butler in the {room}.
    You accuse the Butler: "You are too Butlery for my taste and what are you even doing in the {room} in the first place!"
    
    He just scoffs.
    -> back
  
=== see()
    {
        - docRoom == room:
            The Doc is standing here.
    }
    {
        - sciRoom == room:
            The scientist is here. She’s wearing a lab-coat and goggles.
    }
    {
        - butlerRoom == room:
            The butler is here.
    }
-> DONE

=== people()
	{ docRoom == room:
		+ Talk to the Doc.
		You talk medicine.
		-> back
	}

	{ sciRoom == room:
		+ Talk to the scientist.
		You talk science.
		-> back
	}

	{ butlerRoom == room:
		+ Talk to the butler.
		You talk butlering.
		-> back
	}

=== back
	{
		- room == "hallway": -> hallway
		- room == "kitchen": -> kitchen
		- room == "library": -> library
	}
	
=== function eitherOr(a, b)
    ~ temp x = RANDOM(1, 2)
    {
        - x == 1:
        ~ return a
        
        - x ==  2:
        ~ return b
  	}
4 Likes

I feel like the proposed Rule 4 might be too strict. I agree we don’t want to encourage system elitism or trash talking other’s code, and I’m fine with leaving things unranked, but no comparing code feels like it cuts off room for constructive criticism and closes the door on discussion of relative merits of different systems, which is implied to be some of the motivation of presenting such exercises in the first place.

3 Likes

To the extent that it matters and despite my citing the Codewars site “competition” was not in my mind — rather the idea that prospective users could see how different authors/systems might go about implementing the same common patterns. I perhaps could have cited a different code kata site but I couldn’t remember the one I used back when.

TBH the way I have historically done 4 and 5 in SugarCube is almost exactly the same, with very slight differences depending on if the answer I’m looking for is numeric or alphabetic:

Enter a four-digit combination to open the lock on President Skroob's suitcase:<br>

<<textbox "_answer" "0000">><br>
<<button "Check">>
   <<if _answer.match(/[^0-9]/)>>
   		<<run UI.alert("Enter numerical characters only!")>>
    <<elseif _answer.length != 4>>
    	<<run UI.alert("Enter exactly four digits!")>>
    <<elseif _answer == "1234">>
    	<<replace "#response">>[[The lock pops open.|nextpassage]]<</replace>>
   <<else>>
   		<<replace "#response">>The lock remains shut. This must not be the right combination.<</replace>>
   <</if>>
 <</button>><br><br>
        
<span id="response"></span>

versus

Enter the answer to the clever riddle we'll pretend is here:<br>

<<textbox "_answer" "">><br>
<<button "Check">>
   <<if _answer.match(/[^a-zA-Z]/)>>
   		<<run UI.alert("Enter letters only!")>>
    <<else>>
    	<<set _answer = _answer.toUpperCase()>>
   		<<if _answer == "TUNA">>
   			<<replace "#response">>[[The sphinx moves out of your way.|nextpassage]]<</replace>>
    	<<else>>
            <<replace "#response">>The sphinx says, "Try again."<</replace>>
        <</if>>
    <</if>>
 <</button>><br><br>
        
<span id="response"></span>

But if you wanted a combination lock that “feels” more like a combination lock, at the expense of being slightly more annoying, you could probably use <<cycle>>. Also I’m sure the way I do it (which has not meaningfully changed since Lady Thalia 1 except that some of this stuff is in widgets now) is not the most elegant possible way.

2 Likes

Here’s a Twine-style combination lock I did in Ink for a minor project a while back:

=== hatch ===
VAR d0 = 0
VAR d1 = 0
VAR d2 = 0
VAR d3 = 0
VAR correct_combo = "9274" // note: edited to avoid spoiler
<- room("Hatch",->hatch)
A small hatch labelled CREW ONLY BEYOND THIS POINT.
The hatch is locked with a combination lock.
<strong>[[{d0};;d0]]-[[{d1};;d1]]-[[{d2};;d2]]-[[{d3};;d3]]</strong> #CLASS: combo
+ {current_combo() == correct_combo}[Enter hatch.] -> crew_corridor
+ [d0]
    ~cycle(d0)
+ [d1]
    ~cycle(d1)
+ [d2]
    ~cycle(d2)
+ [d3]
    ~cycle(d3)
+ [Back to promenade.] -> promenade
- -> hatch

=== function cycle(ref val) ===
    ~val = (val + 1) % 10
=== function current_combo() ===
    ~return "{d0}{d1}{d2}{d3}"

Note that I have some JS in my custom template that replaces text of the form [[display text;;choice text]] with a link that triggers the specified choice.

3 Likes

I must note that 2) is a standard feature in TADS3/adv3 and I think isn’t a challenge in this specific language & library.

And what about time available ? Everyone here known well (I hope) that my major WIP indeed is centered on 7) but will be released NET end-2026…

On 3) I actually have partially implemented a bathtub in one of my messing/fooling around playgrounds, but I somewhat “cheated” in having implemented an early XXth century one (= separate hot/cold faucets & taps) so, I guess that implementing a mixing faucet IS a worthwile challenge

on 8) I think that The Portrait is still the extreme in disambiguation challenge, up to that disambiguation lists became an actual QoL for players… (cfr. JJmCC’s review)

that’s all with my sprinkling of .02 euro coins !

Best regards from Italy,
dott. Piergiorgio.

Hmm, is the constructive criticism thing not covered by rule 2? I can see the argument re: discussion of merits of different systems. What do others think?

Having implemented most of these at some time or other, I see this as an interesting challenge that could help authors expand their collection of snippets for possible future use.

The initial list of ideas does seem a little biased towards parser-based games, but that doesn’t mean that choice-based authors can’t attempt it and vice versa.

The only thing I’d suggest is that each challenge should have a set of requirements (just as in software development) so that anyone participating knows exactly what’s required. As an example:

  • Implement a rope.
  • The rope is a portable object, so it can be picked up and dropped.
  • The rope can be tied to an object and untied from the same object.
  • The rope can only be tied to tieable objects.
  • Tieable objects can be fixed in place or portable.
  • The rope conceptually has two ends.
  • If a rope is tied to an object, then that leaves the other end of the rope free.
  • If one end of the rope is tied to an object that is fixed in place:
    • The player can hold the other end of the rope or drop it.
    • When holding the other end of the rope, the player can move to an adjacent room.
    • When holding the end of the rope in an adjacent room, the player can return to the original room, but cannot move any further away without dropping the rope.
  • If the rope is tied to a portable object:
    • The player can hold the tied object and/or the other end of the rope or drop either.
    • If the tied object is held, then both the object and the tied rope move with the player.
    • If the other end of the rope is not held when the player moves, then the other end of the rope is left behind.
    • If the other end of the rope has been left behind, the player can return to the room with the other end of the rope and it will still be there. Alternatively, the player can move to a different room and the other end of the rope will drag behind so that it is now in the room that the player just left.
    • If the player is holding the end of the rope, but not holding the tied object and the player moves, then the behaviour is the same as above, except that the tied object and the end of the rope are reversed.
  • The second end of the rope can be tied to another tieable object in the same room as the first tied object or an adjacent room.

And so on.

I’m not even sure if I’ve captured what you had in mind, hence the importance of clear and explicit requirements. I think the requirements should also specify the grammar to be used (for parser-based games) and whether implicit actions are to be implemented. For example TIE ROPE TO POLE and TIE POLE WITH ROPE should probably be synonymous, but if the player types TIE ROPE, should it implicitly deduce that it’s the pole that’s to be tied? Similarly, if the player types TIE POLE, should it implicitly deduce that it’s to be tied with the rope?

As you can see, even writing the requirements is a challenge. Having implemented several ropes (including a variation of this one), I can assure you that ropes are a real challenge, so it’s interesting that this was the first one on the list.

3 Likes

If the point of this exercise is to facilitate comparison between different approaches and systems (not sure what else it would be), then the current rules do kinda seem like they run the risk of stifling that?

I mean, I understand not being competitive (e.g. “mine is better”) or judging others (e.g. “you should have done X”), but presumably comments like “here’s an alternate solution with the following advantages” or non-cliquey comparisons of systems are kinda the point of this whole thing? But the current rules could be read as prohibiting that, since it’s mostly a list of what not to do.

E.g. if I post an Ink implementation of the Cluedo example, it’s not clear to what extent I can elaborate on the differences between my implementation and the other one already posted without violating the “don’t compare code with others” rule. But I also kinda don’t see what the point would be if I couldn’t point out the differences.

5 Likes

-The rope should be climbable between locations. (If the PC ties the rope to a tree and drops the rope at the top of a cliff, they should be able to climb down and back up between the bottom and top of the cliff. One of the Sir Ramic Hobbs games has an excellent puzzle based on this.)

1 Like

There are lots of different uses for ropes and the implementation will be different depending on the uses. That’s why I think you need to specify the requirements. The example of tying a rope to climb down a cliff is one of the simplest examples of ropes and very easy to implement.

Examples of rope usage (with examples from my games) include:

  • Finding yourself tied up with a rope and needing to find a way to free yourself. (Tin Star)
  • Tying up someone else, typically a bad guy.
  • Using a rope to lead your horse, or similar creature (Tin Star).
  • Using a rope to climb up or down, as in @rovarsson’ example. (Charlie the Chimp, Hangman’s Gulch)
  • Tying a rope to a hook to create a grappling hook, then throwing that over a fence, wall or the branch of a tree to then climb up. (The King’s Ball)
  • Tying a rope to a bucket to lower it down a well to get water. (Hangman’s Gulch)
  • Unravelling a rope to create a thinner cord. (Desperados)
  • Tying two objects together so that they don’t move. (Desperados)
  • Making something else out of the rope. (Santa’s Trainee Elf)
  • Tying to an object to recover it, similar to @pieartsy’s example. (The Time Crystals of Cythii)

And so on.

I don’t think a single rope could ever be used for all these purposes in the same game. At any rate, I was only using the rope as an example and don’t want to turn @pieartsy’s topic into a discussion of ropes.

I think people missed this part, I wasn’t intending for people to try out the prompts without elaborated-upon requirements (not that it’s a problem for those that did…), not to mention potential bonus criteria I was thinking of like “use only built-in functions” or “in the shortest amount of code”.

This is a good list for ropes though!

3 Likes

Fair enough, you’ve convinced me. Do you (or anyone) have a proposed rewording for the rules here? Can’t think of one right now.