# The Prisoners Part 2

Sun Sep 20, 2020

Dawn of the second day.

According to the internet, the thing I intend to build is called a Roguelikelike, teetering on the very edge of being a Roguelike. So it goes; we'll see if I end up taking the title or not.

Last time, we laid out the basics of prisoners, their interactions and their strategies. This time, lets get some different scenarios and some player interaction going.

## Scenarios

Payoff matrices involve deciding who gets what bonus or penalty as a result of an interaction. Given a pair of defect/cooperate choices, a payoff-matrix will return the scores to be delivered to each player in turn.

(defun payoff-matrix (cc-a cc-b cd-a cd-b dc-a dc-b dd-a dd-b)
(let ((tbl {(cons :cooperate :cooperate) (list cc-a cc-b)
(cons :defect :cooperate) (list dc-a dc-b)
(cons :cooperate :defect) (list cd-a cd-b)
(cons :defect :defect) (list dd-a dd-b)}))
(lambda (a b) (lookup tbl (cons a b)))))

Now we can define some basic scenarios. A dilemma is the name I'll pick for the situation where co-operating is better for the group, and both defecting is the worst thing for everyone, but a single defector will end out better off by defecting.

(defparameter dilemma
(payoff-matrix
3 3  1 5
5 1  0 0))

A stag-hunt is a situation where a pair of players can pool their resources for a greater prize, and ignore each other for the lesser. If either player attempts to hunt the stag alone, they get nothing, while their defecting partner still gets a rabbit.

(defparameter stag-hunt
(payoff-matrix
3 3  0 1
1 0  1 1))

A trade is one in which both parties benefit, but to which both parties must agree.

(payoff-matrix
3 3  0 0
0 0  0 0))

A theft is one where a player takes from the other. But if both players cooperate, or both try to rob each other, they come to an impasse.

(defparameter theft
(payoff-matrix
0 0   -3 3
3 -3   0 0))

A trap is a situation where cooperating leads to disaster, ignoring the situation leads to no gain, and defecting to make it clear to your partner that you don't intend to follow ends up benefiting both players.

(defparameter trap
(payoff-matrix
-3 -3  2 2
2  2  0 0))

The last scenario I'll concern myself with is the mutual-prediction. Where guessing what your partner/opponent will choose benefits you, and failing to do so does nothing.

(defparameter mutual-prediction
(payoff-matrix
3 3  0 0
0 0  3 3))

In order to move through the world, our prisoners need a world to move through. Let us begin at the ending.

(defparameter ending
{:description "You have come to the end of your long, perilous journey."})

There is nothing to do at the end other than display this fact.

(format t "~%~%~a~%~%" (lookup adventure :description)))
THE-PRISONERS> (repl! ending)

You have come to the end of your long, perilous journey.

NIL
THE-PRISONERS>

But what led us here was a choice. An adventure is more than a description, it's also the options, a prisoner, the scenario, and a way to continue the action. continueing means making a choice and effectively playing the opposing/cooperating prisoner and abiding by the results.

(let ((prisoner (polo)))
{:description
"A stranger approaches. \"I see you have baubles. Would you like to trade, that we both may enrich ourselves?\""
:cooperate "accept" :defect "refuse" :prisoner prisoner :scenario trade
:continue (lambda (choice)
(let ((their-choice (play prisoner)))
(update! prisoner choice)
ending))}))

This sort of adventure also takes a bit more machinery to run from the repl. We need to present the description, but also get an appropriate choice from the user. Getting that choice is a bit more complicated than you might think at first.

(defun get-by-prefix (lst prefix)
(let ((l (length prefix)))
(loop for elem in lst
when (and (>= (length elem) l)
(== (subseq elem 0 l) prefix))
do (return elem))))

(r-map {(string-downcase (lookup adventure :cooperate)) :cooperate
(by-pref nil)
(resp ""))
(loop until (and (symbolp resp)
(setf by-pref
(get-by-prefix
responses
(string-downcase (symbol-name resp)))))
do (format
t "~a/~a:"
(lookup r-map by-pref)))

Well behaved players are easy to deal with, true...

Accept/Refuse:acc

:COOPERATE
T
Accept/Refuse:ref

:DEFECT
T
Accept/Refuse:a

:COOPERATE
T

... but we want to be a bit more general than that.

Accept/Refuse:fuck you
Accept/Refuse:Accept/Refuse:boo
Accept/Refuse: (error 'error)
Accept/Refuse: (quit)
Accept/Refuse:r

:DEFECT
T
THE-PRISONERS>

That's the only hard par though. Interacting with the game once we're sure we have valid input from our player is relatively simple.

(format t "~%~%~a~%~%" (lookup adventure :description))
(repl! (funcall (lookup adventure :continue) choice)))))

A stranger approaches. "I see you have baubles. Would you like to trade, that we both may enrich ourselves?"

Accept/Refuse:acc

You have come to the end of your long, perilous journey.

NIL
THE-PRISONERS>

This is obviously not the perilous journey being spoken of. At least, not all of it. The simplest way to extend it into one is to wrap scenarios around our existing adventure.

(let ((def (defector)))
{:description "A muscled street thug approachs, knife drawn."
:cooperate "surrender" :defect "run" :prisoner def :scenario theft
:continue (lambda (choice)
(let ((their-choice (play def)))
(update! def choice)
(funcall theft choice their-choice))
(let ((prisoner (polo)))
{:description
"A stranger approaches. \"I see you have baubles. Would you like to trade, that we both may enrich ourselves?\""
:cooperate "accept" :defect "refuse" :prisoner prisoner :scenario trade
:continue (lambda (choice)
(let ((their-choice (play prisoner)))
(update! prisoner choice)
ending))}))}))

A muscled street thug approachs, knife drawn.

Surrender/Run:run

A stranger approaches. "I see you have baubles. Would you like to trade, that we both may enrich ourselves?"

Accept/Refuse:acc

You have come to the end of your long, perilous journey.

NIL
THE-PRISONERS>

Of course, since we want it to be much longer and more perilous, we'll want that process automated to at least some degree.

(insert
scenario
(cons
:continue
(lambda (choice)
(let* ((them (lookup scenario :prisoner))
(their-choice (play them)))
(update! them choice)
(funcall (lookup scenario :scenario) choice their-choice)

(wrap-scenario
(wrap-scenario
ending
{:description
"A stranger approaches. \"I see you have baubles. Would you like to trade, that we both may enrich ourselves?\""
:cooperate "accept" :defect "refuse" :prisoner (polo) :scenario trade})
{:description
"A muscled street thug approachs, knife drawn. \"Yer money or yer life, fop!\""
:cooperate "surrender" :defect "run" :prisoner (defector) :scenario theft}))

This isn't enough for the Roguelikelike title, and I don't think I'll get there today, but I do want the ability to make an arbitrarily long adventure. The dumbest way of doing this is to make a list of scenarios, and pick from them when the need arises.

(defun random-scenario ()
(pick
(list
{:description
"A stranger approaches. \"I see you have baubles. Would you like to trade, that we both may enrich ourselves?\""
:cooperate "accept" :defect "refuse" :prisoner (polo) :scenario trade}
{:description
"A muscled street thug approachs, knife drawn. \"Yer money or yer life, fop!\""
:cooperate "surrender" :defect "run" :prisoner (defector) :scenario theft})))

(loop repeat scenarios

An adventure of even 5 scenarios will end up being repetitive since we currently only have a grand total of two. But we can do something about that...

(defun random-scenario ()
(pick
(list
{:description
"A stranger approaches. \"I see you have baubles. Would you like to trade, that we both may enrich ourselves?\""
:cooperate "accept" :defect "refuse" :prisoner (polo) :scenario trade}
{:description
"A muscled street thug approachs, knife drawn. \"Yer money or yer life, fop!\""
:cooperate "surrender" :defect "run" :prisoner (defector) :scenario theft}
{:description
"As you walk through an expansive market square, a gambler motions you over. \"Fancy your chances at evens or odds?"
:cooperate "Evens!" :defect "Odds!" :prisoner (gambler) :scenario mutual-prediction}
{:description
"A hunter approaches you in a forest clearing. \"Hallo there, young one. Would you help me hunt a deer? I've had enough hares for now, but I promise we'll eat well if we work together!\""
:cooperate "<Nocks bow>" :defect "Rather go my own way" :prisoner (dantes) :scenario stag-hunt}
{:description
"\"Hey follow me into this bear trap!\""
:cooperate "Sure; I've grown tired of living" :defect "No. No, I'd rather not."
:prisoner (robin) :scenario trap}
{:description
"You see a merchant ahead of you, paying little attention to his overfull coin purse. You could cut it and run."
:cooperate "It's too tempting" :defect "No; I hold strong"
:prisoner (dantes) :scenario theft}
{:description
"At the end of your travails with your co-conspirator, you get to the treasure first and can pocket some if you want."
:cooperate "Take it" :defect "No, we split fairly"
:prisoner (gambler :defect 5) :scenario dilemma})))

This gives me some ideas about how to go about generating scenarios a lot more programmatically, but I'll leave that for later, when I'm in the right frame of mind to do cosmetic improvements.

At the end of your travails with your co-conspirator, you get to the treasure first and can pocket some if you want.

Take it/Split fairly:split

You see a merchant ahead of you, paying little attention to his overfull coin purse. You could cut it and run.

It's too tempting/No:it's

"Hey follow me into this bear trap!"

Sure; I've grown tired of living/No. No, I'd rather not.:no

You see a merchant ahead of you, paying little attention to his overfull coin purse. You could cut it and run.

It's too tempting/No:it's

A stranger approaches. "I see you have baubles. Would you like to trade, that we both may enrich ourselves?"

accept/refuse:accept

You have come to the end of your long, perilous journey.

NIL
THE-PRISONERS>

This is about as far as I'm going today, and I'm not entirely sure how far I'm going during my next session.

As always, I'll let you know.