Lisplog

Blogging in Lisp

Search

Feed Aggregator Page 649

Rendered on Mon, 21 Sep 2020 20:33:28 GMT  newer latest older 

Can you use elm-ui or elm-css to make a child element display when hovering over its parent element?

via Elm - Latest posts by @tibastral Thibaut Assus on Mon, 21 Sep 2020 15:37:10 GMT

As hovering over elements is a behavior I prefer to handle that in elm

Elm Radio Episode 13: Make Impossible States Impossible

via Elm - Latest posts by @dillonkearns Dillon Kearns on Mon, 21 Sep 2020 14:07:31 GMT

In this episode, we discuss two game-changing Richard Feldman talks, and how to apply those ideas to your codebase. It was a lot of fun to revisit the talks and do a deep dive on these topics. Hope you enjoy!

And of course, if you haven’t seen the talks, they’re well worth a watch!

Skinney/murmur3 not downloading

via Elm - Latest posts by @razze Kolja Lampe on Mon, 21 Sep 2020 12:43:51 GMT

Totally subjective, but it seems, like we currently get this every week. I think I saw three people mention in slack, that they renamed themselves on github, not sure why that’s so popular right now.

Skinney/murmur3 not downloading

via Elm - Latest posts by @klemola Matias Klemola on Mon, 21 Sep 2020 07:38:11 GMT

Ouch. I saw Robin’s PSA about the name change, but I didn’t consider that his projects could be indirect dependencies. Luckiy elm.json is flexible enough to make those deps into proper deps, at least temporarily.

How to simulate a select

via Elm - Latest posts by @system system on Sun, 20 Sep 2020 12:59:25 GMT

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.

Leo Zovic: The Prisoners Part 2

via Planet Lisp by on Sun, 20 Sep 2020 04:25:30 GMT

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.

(defparameter trade
  (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))

Adventure

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.

(defun repl! (adventure)
  (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.

(defun mk-adventure ()
  (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)
		   (funcall trade choice their-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))))

(defun get-repl-choice (adventure)
  (let* ((responses (mapcar #'string-downcase (list (lookup adventure :cooperate) (lookup adventure :defect))))
	 (r-map {(string-downcase (lookup adventure :cooperate)) :cooperate
		 (string-downcase (lookup adventure :defect)) :defect})
	 (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 adventure :cooperate)
	   (lookup adventure :defect))
       do (setf resp (read)))
    (lookup r-map by-pref)))

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

THE-PRISONERS> (get-repl-choice (mk-adventure))
Accept/Refuse:acc

:COOPERATE
T
THE-PRISONERS> (get-repl-choice (mk-adventure))
Accept/Refuse:ref

:DEFECT
T
THE-PRISONERS> (get-repl-choice (mk-adventure))
Accept/Refuse:a

:COOPERATE
T

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

THE-PRISONERS> (get-repl-choice (mk-adventure))
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.

(defun repl! (adventure)
  (format t "~%~%~a~%~%" (lookup adventure :description))
  (when (contains? adventure :continue)
    (let ((choice (get-repl-choice adventure)))
      (repl! (funcall (lookup adventure :continue) choice)))))
THE-PRISONERS> (repl! (mk-adventure))


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.

(defun mk-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)
				  (funcall trade choice their-choice)
				  ending))}))}))
THE-PRISONERS> (repl! (mk-adventure))


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.

(defun wrap-scenario (adventure scenario)
  (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)
	adventure)))))

(defun mk-adventure ()
  (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})))


(defun mk-adventure (&key (scenarios 5))
  (let ((adventure ending))
    (loop repeat scenarios
       do (setf adventure (wrap-scenario adventure (random-scenario))))
    adventure))

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.

Wanna play a game?

THE-PRISONERS> (repl! (mk-adventure))

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.

Elm Radio Episode 12 - elm-spa

via Elm - Latest posts by @system system on Sun, 20 Sep 2020 07:16:40 GMT

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.

Init a task in a module + avoid circular import?

via Elm - Latest posts by @Yannick971 Yannick on Sat, 19 Sep 2020 23:17:42 GMT

Thanks a lot, very nice solution, I will try this… (its a public API I don’t have access to the backend).
Maybe the title of my post should be changed to something more in tune with the topic actually, I don’t have the required permission.

Init a task in a module + avoid circular import?

via Elm - Latest posts by @pdamoc Peter Damoc on Sat, 19 Sep 2020 05:04:28 GMT

First, if you have 5 sets of data it would be better to model each as a remote data. You can take a look remotedata-http for more information. This assumes that you might want to display partial data (data from some of the 5 endpoints as it arrives). If you want an all or nothing approach, you might also look into Http.task and Task.sequence.

Now, on to the filtering. This logic looks to me like it belongs in the backend BUT, if you don’t have access to the backend and there is no way to call it with some timestamp parameter, you can still do the filtering in the decoding step.

import Json.Decode as Json

type alias Timestamp = 
    { timestamp : Int 
    , value : Int 
    }

oneHour : Int 
oneHour = 3600000

timestampDecoder : Decoder Timestamp
timestampDecoder = 
    Json.map2 Timestamp 
        (Json.field "timestamp" Json.int)
        (Json.field "value" Json.int) 

oneHourFilter : List Timestamp -> List Timestamp 
oneHourFilter timestamps = 
    List.filter (\t -> now + oneHour > t.timestamp ) timestamps

timestampListDecoder : Int -> Decoder (List Timestamp) 
timestampListDecoder now = 
    Json.list timestampDecoder 
        |> Json.map oneHourFilter

You would then get the current time and call the endpoints using (timestampListDecoder now)

getTimestamps : Int -> Cmd Msg
getTimestamps now =
  Http.get
    { url = "https://your-host.com/api/some-timestamps"
    , expect = Http.expectJson GotTimestamps (timestampListDecoder now)
    }

Leo Zovic: The Prisoners Part 1

via Planet Lisp by on Sat, 19 Sep 2020 00:19:48 GMT

Ok, so I guess I'm doing this.

In hopes of participating in the Autumn Lisp 2020 Game Jam, I'm going to write a multiplayer game. It's going to deal with players in several ways, implement 1FA, and probably end up being asymmetric and heavily infulenced by some readings that The Cabal have been doing lately.

But don't worry about that for the moment.

Piece by piece

The basics

(in-package #:the-prisoners)
(named-readtables:in-readtable clj:syntax)
I'm using clj. You can find it on my github, and it'll be included as part of the asd file.

Ahem.

Prisoners can do two things. They can cooperate or they can defect.

(defun coop? (res) (eq :cooperate res))
(defun defe? (res) (eq :defect res))

In order to play a game, you take the game function and apply it to the ordered list of prisoners that will be playing.

(defun play! (game &rest players)
  (apply game players))

A two-player, one-time game looks like this:

  1. We take two prisoners
  2. We ask them to either cooperate or defect
  3. We tell each of them what the other did
  4. We score them

To start with, we're going with a payoff matrix that looks like

          | Cooperate | Defect
------------------------------
Cooperate | 3, 3      | 1, 5
------------------------------
   Defect | 5, 1      | 0, 0
------------------------------

We might play with this later, but lets pretend we won't have the time.

(defun one-time (player-a player-b)
  (let ((a (funcall (lookup player-a :strategy)))
	(b (funcall (lookup player-b :strategy))))
    (if-let (update (lookup player-a :update))
      (funcall update b))
    (if-let (update (lookup player-b :update))
      (funcall update a))
    (cond ((and (coop? a) (coop? b))
	   (list 3 3))
	  ((and (coop? a) (defe? b))
	   (list 1 5))
	  ((and (defe? a) (coop? b))
	   (list 5 1))
	  (t
	   (list 0 0)))))

The two simplest possible prisoners we can have are one who always :cooperates, and one who always :defects. A prisoner needs to be able to take into account what their opponent did last time, and separately, do something.

(defun defector ()
  {:name :defector :strategy (lambda () :defect)})

(defun cooperator ()
  {:name :cooperator :strategy (lambda () :cooperate)})

We can now play. Would you like to play a game?

The Simplest Game

Wanna play a game?

THE-PRISONERS> (play! #'one-time (defector) (cooperator))
(5 1)
THE-PRISONERS> (play! #'one-time (cooperator) (defector))
(1 5)
THE-PRISONERS> (play! #'one-time (cooperator) (cooperator))
(3 3)
THE-PRISONERS> (play! #'one-time (defector) (defector))
(0 0)
THE-PRISONERS>

There are other, simple kinds of prisoners. One is the prisoner who tosses a coin and does what it tells them to.

(defun gambler ()
  {:name :gambler :strategy (lambda () (nth (random 2) (list :cooperate :defect)))})

The more general case doesn't necessarily flip a coin, but can weigh either :cooperate or :defect more strongly.

(defun gambler (&key (cooperate 1) (defect 1))
  (let ((total (+ cooperate defect))
	(moves (concatenate
		'list
		(loop repeat cooperate collect :cooperate)
		(loop repeat defect collect :defect))))
    {:name (intern (format nil "GAMBLER~a/~a" cooperate defect) :keyword)
	   :strategy (lambda () (nth (random total) moves))}))

This way, we can get a true coin-flipper.

THE-PRISONERS> (gambler)
{:NAME :GAMBLER1/1 :STRATEGY #<CLOSURE (LAMBDA () :IN GAMBLER) {1003B5824B}>}
THE-PRISONERS>

Or someone who mostly cooperates/defects, but sometimes defects/cooperates.

THE-PRISONERS> (gambler :cooperate 5)
{:NAME :GAMBLER5/1 :STRATEGY #<CLOSURE (LAMBDA () :IN GAMBLER) {1003B69F0B}>}
THE-PRISONERS> (gambler :defect 5)
{:NAME :GAMBLER1/5 :STRATEGY #<CLOSURE (LAMBDA () :IN GAMBLER) {1003B6C38B}>}
THE-PRISONERS>

How do they play against each of the others? Lets find out.

The Second Simplest Game

(defun matches (elems &key (mirror? t))
  (loop for (a . rest) on elems while rest
      if mirror? collect (cons a a)
      append (loop for b in rest collect (cons a b))))

(defun all-against-all! (game matches)
  (reduce
   (lambda (memo res)
     (merge-by #'+ memo res))
   (loop for (a . b) in matches
      collect (let ((res (play! game a b)))
		{(lookup a :name) (first res) (lookup b :name) (second res)}))))

This lets us see who does better against everyone.

THE-PRISONERS> (all-against-all! #'one-time (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 13 :GAMBLER1/1 9 :GAMBLER5/1 8 :DEFECTOR 10 :COOPERATOR 8}
THE-PRISONERS> (all-against-all! #'one-time (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 8 :GAMBLER1/1 7 :GAMBLER5/1 8 :DEFECTOR 15 :COOPERATOR 10}
THE-PRISONERS> (all-against-all! #'one-time (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 10 :GAMBLER1/1 7 :GAMBLER5/1 8 :DEFECTOR 15 :COOPERATOR 8}
THE-PRISONERS> (all-against-all! #'one-time (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 11 :GAMBLER1/1 10 :GAMBLER5/1 11 :DEFECTOR 10 :COOPERATOR 6}
THE-PRISONERS>

The defector comes out on top here. And the mostly-defecting gambler doesn't do bad either. Of course, this is what we would expect from the one-time game.

An iterated game is like a series of one-time games, and it keeps a running total of the score.

(defun iterated (&key (iterations 10))
  (lambda (player-a player-b)
    (loop repeat iterations
       for (a b) = (one-time player-a player-b)
       sum a into a-sum sum b into b-sum
       finally (return (list a-sum b-sum)))))

It plays about how you'd expect

THE-PRISONERS> (play! (iterated) (defector) (cooperator))
(50 10)
THE-PRISONERS> (play! (iterated) (cooperator) (cooperator))
(30 30)
THE-PRISONERS> (play! (iterated) (defector) (defector))
(0 0)
THE-PRISONERS>

And setting the world at its' own throat works the way you'd expect of this process so far.

THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 119 :GAMBLER1/1 117 :GAMBLER5/1 105 :DEFECTOR 135 :COOPERATOR 100}
THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 132 :GAMBLER1/1 109 :GAMBLER5/1 103 :DEFECTOR 120 :COOPERATOR 100}
THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5))))
{:GAMBLER1/5 100 :GAMBLER1/1 124 :GAMBLER5/1 92 :DEFECTOR 130 :COOPERATOR 96}
THE-PRISONERS>

There are more elaborate strategies we can call upon. I won't implement them all here, but these have been thought of.

Thoughtful Players

Robin alternates between cooperating and defecting.

(defun robin ()
  (let ((prev :cooperate))
    {:name :robin
	   :strategy (lambda ()
		       (if (coop? prev)
			   (setf prev :defect)
			   (setf prev :cooperate)))}))

And then, there are the simplest strategies that consider their opponent.

(defun polo ()
  (let ((prev nil))
    {:name :polo
	   :update (lambda (opponent-action) (setf prev opponent-action))
	   :strategy (lambda () (or prev :cooperate))}))

(defun dantes ()
  (let ((plan :cooperate))
    {:name :dantes
	   :update (lambda (action) (when (defe? action) (setf plan :defect)))
	   :strategy (lambda () plan)}))

With the addition of these, it's no longer obviously a defectors game.

THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 164 :DANTES 131 :GAMBLER1/1 150 :GAMBLER5/1 169 :DEFECTOR 150 :COOPERATOR 184 :POLO 120 :ROBIN 147}
THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 168 :DANTES 126 :GAMBLER1/1 176 :GAMBLER5/1 159 :DEFECTOR 165 :COOPERATOR 184 :POLO 129 :ROBIN 136}
THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 158 :DANTES 121 :GAMBLER1/1 154 :GAMBLER5/1 156 :DEFECTOR 150 :COOPERATOR 184 :POLO 123 :ROBIN 154}
THE-PRISONERS> (all-against-all! (iterated) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 163 :DANTES 131 :GAMBLER1/1 163 :GAMBLER5/1 161 :DEFECTOR 175 :COOPERATOR 184 :POLO 117 :ROBIN 146}
THE-PRISONERS> (all-against-all! (iterated :iterations 50) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 789 :DANTES 656 :GAMBLER1/1 940 :GAMBLER5/1 964 :DEFECTOR 720 :COOPERATOR 1056 :POLO 585 :ROBIN 752}
THE-PRISONERS> (all-against-all! (iterated :iterations 50) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 845 :DANTES 651 :GAMBLER1/1 892 :GAMBLER5/1 959 :DEFECTOR 775 :COOPERATOR 1054 :POLO 609 :ROBIN 719}
THE-PRISONERS> (all-against-all! (iterated :iterations 50) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 788 :DANTES 651 :GAMBLER1/1 929 :GAMBLER5/1 946 :DEFECTOR 775 :COOPERATOR 1044 :POLO 609 :ROBIN 744}
THE-PRISONERS> (all-against-all! (iterated :iterations 50) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 859 :DANTES 651 :GAMBLER1/1 867 :GAMBLER5/1 952 :DEFECTOR 765 :COOPERATOR 1048 :POLO 609 :ROBIN 729}
THE-PRISONERS> (all-against-all! (iterated :iterations 50) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 833 :DANTES 666 :GAMBLER1/1 920 :GAMBLER5/1 953 :DEFECTOR 775 :COOPERATOR 1046 :POLO 603 :ROBIN 720}
THE-PRISONERS> (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 8325 :DANTES 6436 :GAMBLER1/1 9255 :GAMBLER5/1 9544 :DEFECTOR 7565 :COOPERATOR 10508 :POLO 8976 :ROBIN 7383}
THE-PRISONERS> (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 8365 :DANTES 6531 :GAMBLER1/1 9289 :GAMBLER5/1 9531 :DEFECTOR 7645 :COOPERATOR 10486 :POLO 6018 :ROBIN 7379}
THE-PRISONERS> (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 8407 :DANTES 6546 :GAMBLER1/1 9139 :GAMBLER5/1 9574 :DEFECTOR 7590 :COOPERATOR 10554 :POLO 6117 :ROBIN 7389}
THE-PRISONERS> (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 8063 :DANTES 6371 :GAMBLER1/1 9231 :GAMBLER5/1 9492 :DEFECTOR 7555 :COOPERATOR 10508 :POLO 6084 :ROBIN 7412}
THE-PRISONERS> (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 8068 :DANTES 6456 :GAMBLER1/1 9165 :GAMBLER5/1 9614 :DEFECTOR 7395 :COOPERATOR 10516 :POLO 6003 :ROBIN 7451}
THE-PRISONERS> (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin))))
{:GAMBLER1/5 8241 :DANTES 6356 :GAMBLER1/1 9150 :GAMBLER5/1 9579 :DEFECTOR 7545 :COOPERATOR 10480 :POLO 9021 :ROBIN 7392}
THE-PRISONERS>

When it's a prisoner against the world, the makeup of the world makes a difference in which prisoner ultimately wins.

(defun winner (results)
  (let ((max nil)
	(score nil))
    (loop for (k . v) in (as-list results)
       do (if (or (not score) (> v score))
	      (setf score v
		    max (cons k v))))
    max))

Currently, with mirror matches happening, the world is tilted towards cooperators.

THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10554)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10532)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10486)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10536)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10478)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10502)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10540)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10516)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)))))
(:COOPERATOR . 10476)
THE-PRISONERS>

Without mirror matches, it's still mostly a cooperators' game, but not quite so strongly.

THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))

(:DEFECTOR . 7665)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:ROBIN . 7497)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:COOPERATOR . 7512)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:COOPERATOR . 7580)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:COOPERATOR . 7516)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:COOPERATOR . 7528)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:DEFECTOR . 7615)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:DEFECTOR . 7610)
THE-PRISONERS> (winner (all-against-all! (iterated :iterations 500) (matches (list (cooperator) (defector) (gambler) (gambler :cooperate 5) (gambler :defect 5) (polo) (dantes) (robin)) :mirror? nil)))
(:COOPERATOR . 7550)
THE-PRISONERS>

This wasn't the end. It was step one.

Init a task in a module + avoid circular import?

via Elm - Latest posts by @Yannick971 Yannick on Sat, 19 Sep 2020 00:21:36 GMT

Thanks @pdamoc and @joelq for your help! The way I tried to design this is so messy that its hard to explain, huge red flag! :slight_smile: I am still experimenting with Elm and FP.

So maybe its better to explain what I would like to do and maybe you can share with me how you’d design this?

Main.Model holds data decoded from 5 different HTTP requests, that I got it to work well thanks to your advice in this chat:

type Model =
  Loading {
    dataA: Maybe DataA
    ,dataB: Maybe DataB
    , ....etc...

}
| Success {
    dataA: DataA
    ,dataB: DataB
    , .... etc...

}
| Error String

That works beautifully… but of course I don’t want to hold the whole API response or data I dont need in Main.Model.
So, Main.init initiates the API calls, then I import the decoders from the respective modules DataA.elm etc… to parse the responses.
The responses I will get back are nested timeseries data, with hourly timestamps, and for each I am only interested in the value matching the next hour’s timestamp.

...  [
{timestamp: 1231243124, value: 23},
{timestamp: 1231243453, value: 12},
]

So I get this array back, at the decoder level, now how can I “filter in” only the value I am interested in, (the one corresponding to the next hour after I ran the app) ? I just want to update the model with, lets say, 23.

First I will have to first compute the timestamp corresponding to the next hour in my local timezone. I think I am good with that part but where should this timestamp be computed? Main or separate module?
And then, most importantly, how do I share this timestamp with all the 5 decoders. Is it possible or a good idea to pass it as an argument to the decoders? or should I make a function that takes the time and return a Decoder?

I guess that is what really confuses me… I feel that the filtering should happen at the decoder level, so I have a very clean model, but I don’t know how to do this more advanced decoding using an outside parameter… the only way I would be able to do it now is to store the whole lists of data in Model and then filter it before Main.view, but that’s nasty :).

I hope it’s more clear… thank you.

Alexander Artemenko: eco

via Planet Lisp by on Fri, 18 Sep 2020 20:52:26 GMT

This template engine is interesting because it allows mixing lisp code blocks and HTML in a way simple enough to be used by non-lisp developers and designers.

It's interesting feature is that each template definition includes the arguments list.

Here is how we can define templates for user list from the previous post about cl-emb:

POFTHEDAY> (eco:compile-string
            "
<% deftemplate user (nickname name) () %>
<a href=\"/users/<%= nickname %>\"><%= name %></a>
<% end %>
")

POFTHEDAY> (eco:compile-string "
<% deftemplate user-list (users) () %>
<ul>
  <% loop for (nickname name) in users do %>
    <li><%- user nickname name %><% end %></li>
  <% end %>
</ul>
<% end %>
")

POFTHEDAY> (eco-template:user-list
            '(("bob" "Bob Hopkins")
              ("alice" "Alice Cooker")))
"
<ul>
  
    <li>
<a href=\"/users/bob\">Bob Hopkins</a>
</li>
  
    <li>
<a href=\"/users/alice\">Alice Cooker</a>
</li>
  
</ul>
"

Also, there is a way to load templates from the files with .eco extensions. There is an ASDF extension which allows defining these templates as components of your ASDF system.

Documentation does not cover this, but the template components should be defined like this:

(defsystem mysite
  :defsystem-depends-on (eco)
  :components ((:module "src"
                :depends-on "templates"
                :components ((:file "backend-code")
                             (:file "utils")))
               (:module "templates"
                :components ((:eco-template "index-page")
                             (:eco-template "users")))))

Well, let's measure Eco's performance!

POFTHEDAY> (eco:compile-string "
<% deftemplate perform (title items) () %>
<title><%= title %></title>
<ul>
  <% loop for item in items do %>
    <li><%= item %></li>
  <% end %>
</ul>
<% end %>
")

POFTHEDAY> (time
            (loop repeat 1000000
                  do (eco-template:perform "Foo Bar"
                       '("One" "Two" "Three"))))
Evaluation took:
  2.135 seconds of real time
  2.144360 seconds of total run time (2.121050 user, 0.023310 system)
  [ Run times consist of 0.141 seconds GC time, and 2.004 seconds non-GC time. ]
  100.42% CPU
  4,713,480,570 processor cycles
  1,008,017,904 bytes consed

This is slower than half of the tested template engines. It took place between cl-who and print-html. I've expected it will be faster :(

"fold" animation in elm-css

via Elm - Latest posts by @hexedhash Giorgio on Fri, 18 Sep 2020 15:46:09 GMT

@francescortiz Thanks for the suggestion. However I need the bounding box to also reduce in size throughout the CSS transition :frowning:

"fold" animation in elm-css

via Elm - Latest posts by @hexedhash Giorgio on Fri, 18 Sep 2020 15:45:11 GMT

I’m beginning to think that I’m going to need to add an animation library. But because of the optimizations that elm does to reduce asset size, I think my concerns are less relevant - as long as the API for Alert remains simple and intiutive.

Skinney/murmur3 not downloading

via Elm - Latest posts by @hexedhash Giorgio on Fri, 18 Sep 2020 15:38:01 GMT

Thanks @evancz!

For transparency, this was the fix for me:

Which included elm-css’s change.

murmur3 was an indirect dependency via elm-css. I had to manually update my elm.json to remove the stale reference to murmur3.

Elm-review 2.3.0 - "Just try it out"

via Elm - Latest posts by @system system on Fri, 18 Sep 2020 15:23:44 GMT

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.

Init a task in a module + avoid circular import?

via Elm - Latest posts by @joelq Joël Quenneville on Fri, 18 Sep 2020 14:45:59 GMT

Reading between the lines, it sounds like you are trying to build your project using a “fractal architecture” with a bunch of modules that each define init/update/view, etc. Most extracted modules in Elm apps are not structured this way. Instead, they are usually just a data structure and some functions that act upon it (think the Elm core library). The init/update/view functions are not magic framework functions and modules don’t need to implement them.

It’s hard to make an definite recommendations without more context on what you are doing but most solutions likely involve passing in the time as an argument as suggested by @pdamoc.

For example you might create some sort of constructor for your custom data structure in the module like:

MyModule.fromTime : Posix -> MyModule.Structure

which you could call from Main like:

module Main exposing(main)

import MyModule

init : Flags -> (Model, Cmd Msg)
init flags =
  (initialModel, Task.perform GotInitialTime Time.now)

update : Msg -> Model -> (Model, Cmd Msg)
update msg model  =
  case msg of
    GotInitialTime time ->
      ({ model | structure = MyModule.fromTime time }, Cmd.none)

Parsers with Error Recovery

via Elm - Latest posts by @rupert Rupert Smith on Fri, 18 Sep 2020 12:25:58 GMT

So I feel like I am now making some good progress with this. I have taken Matt’s TolerantParser as a starting point, the current WIP is here:

https://github.com/the-sett/parser-recoverable

Example

Don’t get too excited, but I created a little example to start trying it out:

cd examples
elm-live src/Main.elm

Parsing Outcomes

I didn’t like that the result of run is going to yield a double wrapped Result, the outer one with DeadEnds but the inner one with just problem.

type alias Parser context problem data =
    Parser.Parser context problem (Result (List problem) data)

outcome : Result (List (DeadEnd c x)) (Result (List x) data)
outcome = Parser.Advaned.run someParser someInput

So I changed the inner error representation to be (DeadEnd c x) too. Only problem with that is that Parser.Advanced has no getContext function, so I could not give a context stack. Errors resulting from inside the extended parser will therefore have not have the full context - perhaps there is a way to do it by carrying around a second copy of the context stack with the parser? I guess I’ll try and fix that when it actually looks like it is needed.

The other thing is that I didn’t think Result was the right output for this parser to give. If the parser does recover succesfully it will still yield some AST, but it should also give errors for the bits it had to gloss over to get it. So the outcome should be more like a tuple.

I decided to use this rather than (List (DeadEnd c x), Maybe data), to avoid the case where there are no errors and no data!

{-| Describes the possible outcomes from running a parser.

    - `Success` means that the parsing completed with no syntax errors at all.
    - `Partial` means that the parsing was able to complete by recovering from
    syntax errors. The syntax errors are listed along with the parsed result.
    - `Failure` means that the parsing could not complete, so there is no parsed
    result, only a list of errors.

-}
type Outcome context problem value
    = Success value
    | Partial (List (DeadEnd context problem)) value
    | Failure (List (DeadEnd context problem))

The run function is then:

run : Parser c x a -> String -> Outcome c x a

Recovery Tactics

Similar to the TolerantParser, I defined a set of actions to guide the parser when it fails.

{-| Describes the possible ways the parser should act when it encounters
something that it cannot parse.

    - `Fail` stop parsing and return a `Failure` outcome.
    - `Warn` ignore the error, but add a problem and use a `Partial` outcome.
    - `Ignore` ignore the error and continue with a `Success` outcome.
    - `ChompForMatch` try chomping to find a matching character. If succesfull
    add a problem but continue with a `Partial` outcome. If this does not work
    then `Fail`.

-}
type RecoveryTactic x
    = Fail
    | Warn x
    | Ignore
    | ChompForMatch (List Char) x

The default behaviour is to Fail.

The current recovery tactic can be attached to a parser with this function:

withRecovery : RecoveryTactic x -> Parser c x a -> Parser c x a

The idea is that this will be passed down to all subsequent parsers (chained with ignore, keep, map, andThen, and so on), and not just for one particular token. So if parsing a List Int, but some of the Ints are expressions, the parser could keep its strategy of chomping to the end of the list or next comma, in the event that it sees a malformed expression. Not totally sure this is the right thing, but it feels right for now.

Feedback to the Editor

This is what I am going to work on next, by evolving the problem type.

When a string gets chomped to recover, I will add the start position of the string, and the string itself to the problem. The idea is that an editor can check if a problem overlaps the current cursor position, and if so, it knows what String to cut out of the source and offer context sensitive suggestions to replace it with.

"fold" animation in elm-css

via Elm - Latest posts by @eimfach Robin G. on Fri, 18 Sep 2020 11:58:18 GMT

Using CSS Transitions on Auto Dimensions

According to the Mozilla Developer Network docs, auto values have been intentionally excluded from the CSS transitions spec. It looks like it’s been requested by a few people, but when you think about it, it makes at least a little sense that it hasn’t been included. The browser process that re-calculates the sizes and positions of all elements based on their content and the way they interact with each other (known as “reflow”) is expensive. If you were to transition an element into a height of auto , the browser would have to perform a reflow for every stage of that animation, to determine how all the other elements should move. This couldn’t be cached or calculated in a simple way, since it doesn’t know the starting and/or ending values until the moment the transition happens. This would significantly complicate the math that has to be done under the hood and probably degrade performance in a way that might not be obvious to the developer.

Seeking advice on which Haskell back end frameworks/libraries to use

via Elm - Latest posts by @system system on Fri, 18 Sep 2020 11:09:43 GMT

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.

 newer latest older