la

Atlanta August 2007 12/23















la

Leo Zovic: Places, Peeps And Plagues

(in-package #:cl-pestilence)

;;   This is _not_ a simulation. It's just a game. And any resemblance
;; to any world, real or imaginary, is entirely coincidental.

;;   You can copy/paste this post in its entirety into a Common Lisp
;; REPL and play around with it if you like. I'm documenting it where
;; possible, but it's just a small toy to poke at for the moment.

;;   I've been thinking a lot about asymmetric multiplayer games and
;; <gestures wildly to world at large> all this.
;; I'm not actively _trying_ to model it accurately, but it's probably
;; obvious what's been consuming my thoughts lately.

;;   Let's get right into this. I'll explain as I go, and tie a few things
;; together neatly at the end. I hope. Regardless, there will absolutely
;; be a repo sometime fairly soon.

;; A place can be tagged arbitrarily, and can contain occupants.
;; They also collect points.

(defclass place ()
  ((tags :initarg :tags :initform nil :accessor tags)
   (occupants :initarg :occupants :initform nil :accessor occupants)
   (points :initform 0 :accessor points)))

(defun place? (thing)
  (eq (find-class 'place) (class-of thing)))

(defun place (&key tags occupants)
  (make-instance 'place :tags tags :occupants occupants))

(defun gen-place ()
  (let ((tag (pick '(:apartment-building :house :cottage
		     :office-building :factory :store
		     :cafe :lounge :theater))))
    (place :tags (list tag))))

(defmethod details ((place place))
  (format nil "====================~%~a {~{~a~}}~%~{  ~a~^~%~}~%"
	  (first (tags place))
	  (rest (tags place))
	  (mapcar #'details (occupants place))))

(defmethod show ((place place))
  (format nil "~20@a ~5a [~{~a~}]~%"
	  (first (tags place)) (points place)
	  (mapcar #'show (occupants place))))

;; A peep goes places.
;; They have
;;  - their daily routine (a list of places to visit)
;;  - their todo (the part of their routine they still need to do;
;;                they are currently at the first place in this list)
;;  - their health (a number from 0 to 100)
;;  - a list of plagues
;; Finally, they _also_ collect points.

(defclass peep ()
  ((routine :initarg :routine :initform (list) :accessor routine)
   (todo :initarg :todo :initform nil :accessor todo)
   (health :initarg :health :initform 100 :accessor health)
   (plagues :initform nil :accessor plagues)
   (points :initform 0 :accessor points)))

(defun peep? (thing)
  (eq (find-class 'peep) (class-of thing)))

(defun peep (&key places)
  (make-instance 'peep :routine places :todo places))

(defun health->string (health)
  (cond ((>= health 90) "@")
	((>= health 80) "0")
	((>= health 70) "O")
	((>= health 50) "o")
	((>= health 30) ":")
	((>= health 1)  ".")
	(t "☠")))

(defmethod details ((peep peep))
  (format nil "[~a ~3d [~{ ~a~^ ->~}]]"
	  (health->string (health peep)) (health peep)
	  (mapcar
	   (lambda (place) (first (tags place)))
	   (routine peep))))

(defmethod show ((peep peep)) (health->string (health peep)))

;; A world is a list of places, occupied by peeps. The world we start
;; peeps in also determines their routine.

(defun gen-world (&key (num-places 20) (num-peeps 100))
  (let ((places (loop repeat num-places collect (gen-place))))
    (loop repeat num-peeps
       do (let* ((routine (loop repeat 5 collect (pick places)))
		 (peep (peep :places routine)))
	    (push peep (occupants (first routine)))))
    places))

(defmethod details ((world list))
  (format nil "~%~{~a~}~%" (mapcar #'details world)))

(defmethod show ((world list))
  (format nil "~%~{~a~}~%" (mapcar #'show world)))

(defmethod all-peeps ((world list))
  (loop for place in world append (all-peeps place)))

(defmethod all-peeps ((place place))
  (loop for o in (occupants place) if (peep? o) collect o))

;; `tick!`ing a world means moving every peep through their routine once.
;;   We `tick!` each peep, then `tick!` each place until all the peeps are
;; done. Then we reset their routines.
;; You can think of this as a turn in the game.

(defmethod tick! ((world list))
  (let ((peeps (all-peeps world)))
    (loop while peeps
       do (setf peeps
		(loop for p = (pop peeps) while p
		   for res = (tick! p)
		   if res collect res))
       do (mapc #'tick! world)
       do (format t "~a" (show world)))
    (loop for p in (all-peeps world)
       do (setf (todo p) (routine p))))
  world)

;; Don't worry about the details of how to `tick!` peeps or places yet.

;;   Ok, here's where it gets a bit darker. Although we _did_
;; foreshadow this in the definition of `peep`. And also in the title
;; of the accompanying blog post.

;; A plague is another living thing.
;; It has
;;  - a host (a peep that it's infecting)
;;  - a signature (a token representing its lineage and strain)
;;  - health (how well it's doing inside its host)
;;  - virulence (how likely it is to spread to another host)
;;  - efficiency (how efficient they are at feeding)
;;  - reproduce (a function that returns a new instance to push into a new host)
;;  - and a strategy (a function, possibly closed, that takes
;;    itself and its host peep and mutates)

;; Plagues do not collect points; they score differently.

(defclass plague ()
  ((host :initarg :host :initform nil :accessor host)
   (signature :initarg :host :initform "SIG" :accessor signature)
   (health :initarg :health :initform 10 :accessor health)
   (virulence :initarg :virulence :initform 10 :accessor virulence)
   (efficiency :initarg :efficiency :initform 0.2 :accessor efficiency)
   (reproduce
    :initarg :reproduce
    :initform
    #'plague
    :reader reproduce)
   (strategy
    :initarg :strategy
    :initform
    (lambda (plague peep)
      (feed! plague peep 30))
    :reader strategy)))

(defun plague ()
  (make-instance 'plague))

;; Plagues can `feed!` on peeps or plagues. To feed means to
;; take away some of the targets' health and add some to your own.

(defmethod feed! ((self plague) (peep peep) (amount integer))
  (decf (health peep) amount)
  (incf (health self) (* (efficiency self) amount)))

(defmethod feed! ((self plague) (plague plague) (amount integer))
  (decf (health plague) amount)
  (incf (health self) (* (efficiency self) amount)))

;; Plagues can also `infect!` peeps by `reproduce`ing into them.

(defmethod infect! ((self plague) (peep peep))
  (unless (infected-by? self peep)
    (let ((child (funcall (reproduce self))))
      (setf (host child) peep)
      (push child (plagues peep)))))

(defmethod infected-by? ((self plague) (peep peep))
  (member (signature self) (mapcar #'signature (plagues peep))
	  :test #'string=))

;;  `tick!`ing a plague causes it to weaken and also carry out its strategy.
;; This models the background effect of the immune system of its host.

(defmethod tick! ((plague plague))
  (decf (health plague) 1)
  (funcall (strategy plague) plague (host plague))
  plague)

;;  `tick!`ing a peep means moving them to their next place, and also
;; `tick!`ing any plagues they may have contracted. Also, peeps are
;; resilient; they heal a small amount each time they tick (to a
;; maximum of 100).
;;  If a peep dies, they no longer move. And their plagues probably
;; won't do well. Peeps like to go places. They score points for each
;; place they go to.

(defun dead? (thing) (>= 0 (health thing)))

(defmethod tick! ((peep peep))
  (unless (dead? peep)
    (let ((location (pop (todo peep))))
      (incf (points peep))
      (setf (occupants location) (remove peep (occupants location)))
      (push peep (occupants (or (first (todo peep)) (first (routine peep)))))
      (setf (health peep) (min 100 (+ 5 (health peep))))
      (mapc #'tick! (plagues peep))
      (unless (empty? (todo peep))
	peep))))

;; `tick!`ing a place causes it to score for each `peep` present. And it causes
;; any `plague`s on present `peep`s to try to `infect!` other nearby peeps.
;; Places also lose points for each dead peep they contain.

(defmethod tick! ((place place))
  (incf (points place) (length (occupants place)))
  (loop for peep in (all-peeps place)
     if (dead? peep)
     do (decf (points place) 2)
     else do (loop for plague in (plagues peep)
		do (loop for victim in (remove peep (all-peeps place))
		      if (>= (virulence plague) (random 100))
		      do (infect! plague victim))))
  place)

;;  So, now we've got the basic framework of the game in place. There are three
;; players in this game: places, peeps and plagues.
;;   A plague player automatically loses if they are completely cured, and
;; automatically wins if they manage to kill everyone. That's fairly simple.
;;   A place player wins if they manage to cure the plague. They automatically
;; lose if all the peeps die. Also, fairly simple.
;;   A peep player is trying to survive. If they manage to make it some numer
;; of turns before dying, then we have to score the game instead of declaring
;; an outright winner regardless of game state.

;;   A peep player's score is the total number of points plus remaining health
;; on all of their peeps, minus the number of active plagues on said peeps.
;;   A plague player's score is the total number of health of their plagues,
;; with a multiplier equal to the number of places fully infected by
;; their plague.
;;   A place player's score is the total number of points in their places.

(defun score (world)
  (list :peep (let ((score 0))
		(loop for p in (all-peeps world)
		   unless (dead? p)
		     do (incf score (+ (health p) (points p)))
		   do (decf score (length (plagues p))))
		score)
	:place (let ((score 0))
		 (loop for p in world
		    do (incf score (points p)))
		 score)
	:plague (let ((score 0))
		  (loop for victim in (all-peeps world)
		     do (loop for p in (plaguesvictim)
			   do (incf score (max 0 (health p)))))
		  (loop for target in world
		     if (every
			 (lambda (victim)
			   (not (empty? (plagues victim))))
			 (all-peeps target))
		     do (setf score (* 2  score)))
		  score)))

;;   I think that's all I've got for now. This is definitely an idea I want
;; to run with. At the moment, it's just a tiny, in-repl proof-of-concept,
;; and not particularly fun, but I'm going to try developing it further with an
;; eye towards turning it into an actual web game playable from this site.

;; As always, I'll let you know how it goes.

(defun pick (lst)
  (nth (random (length lst)) lst))

(defun empty? (lst)
  (null lst))




la

Quicklisp news: April 2020 Quicklisp dist update now available

New projects:

  • anypool — General-purpose pooling library — BSD 2-Clause
  • avl-tree — An implementation of the AVL tree data structure. — MIT
  • cl-aubio — Aubio bindings for Common Lisp — GPLv3
  • cl-interval — Intervals, interval trees — NewBSD, LLGPL
  • cl-liballegro — Allegro 5 game programming library bindings for Common Lisp — Allegro 5 - http://alleg.sourceforge.net/license.html
  • cl-mime-from-string — A one function library to return a mime-type based on the file extension found at the end of a string. ie abc.txt -> text/plain. The common types implemented are from https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types — MIT
  • cl-telegram-bot — Telegram Bot API, based on sovietspaceship's work but mostly rewritten. — MIT
  • dns-client — A client for the DNS protocol. — zlib
  • feeder — RSS, Atom and general feed parsing and generating — zlib
  • perceptual-hashes — Perceptual hash algorithms for images — 2-clause BSD
  • portable-condition-system — A portable condition system for Common Lisp — CC0
  • ten — Template System for Common Lisp — MIT
  • trivial-custom-debugger — Allows arbitrary functions to become the standard Lisp debugger — MIT
  • trivial-with-current-source-form — Helps macro writers produce better errors for macro users — GPLv3
  • vom-json — A json-formatted logger for vom — MIT
  • vp-trees — Perceptual hash algorithms for images — 2-clause BSD
Updated projects3b-bmfont3bgl-shader3bmd3bza-cl-loggeralexandriaaprilasync-processbdefbpccldocchungacl+sslcl-anacl-capstonecl-cffi-gtkcl-collidercl-containerscl-environmentscl-gamepadcl-gservercl-inotifycl-marklesscl-packcl-patternscl-pythoncl-rdkafkacl-shlexcl-sparqlcl-strcl-tuicl-utilscl-webkitclinenoiseclipcloser-mopconcrete-syntax-treecroatoancserial-portdartscltoolsdefenumdeploydexadordiff-match-patchdissectdjuladoubly-linked-listeasy-routeseclectorescalatorfast-generic-functionsfast-ioflexi-streamsflexichainfloat-featuresfsetfuccfunctional-treesfxmlgendlgraphgtirbhu.dwim.computed-classhu.dwim.defhu.dwim.perechu.dwim.presentationhu.dwim.quasi-quotehu.dwim.walkerhu.dwim.web-serverhunchentoot-multi-acceptorironcladkeystonelispqrliterate-lispmaidenmaxpcmcclimmmapmodularizemutilitynodguinumclnumpy-file-formatoriginosicatoverlordparachutepatchworkpetalisppetriphoe-toolboxplumppolicy-condpolisherpostmodernpzmqqtoolsquilcqvmroanrpcqs-graphvizs-http-clients-http-servers-sysdepss-utilssanity-clausescalplsealable-metaobjectsselselect-fileserapeumsketchskippy-renderersnappysoftdrinkspinneretstaplestumpwmsucleswank-clientswank-crewtootertrace-dbtrivial-featurestrivial-file-sizevgplotwoo.

Removed projects: cl-password-store, fomus, rfc3339-timestamp, rpc4cl.

All the removed projects are removed because they no longer build. For the first two (cl-password-store and fomus), I was unable to get a response from the authors. The other two (rfc3339-timestamp, rpc4cl) the author was responsive, but has abandoned the projects.

To get this update, use (ql:update-dist "quicklisp"). Enjoy!

A number of people support Quicklisp with a monthly contribution through PayPal. I recently set up a Quicklisp Patreon page as an alternative - if you are interested in supporting Quicklisp, feel free to check it out.




la

Nicolas Hafner: Creative Block - May Kandria Update


It's a new month, and that usually means I'm supposed to write a monthly update on the progress with Kandria. Thinking about that though made me feel very depressed because I realised that I hadn't really done anything at all for the game, all of April.

I can blame however much I want of that on the quarantine and university stress, or whatever else, but it won't change the fact that there has not been much progress on any front. While I have been slacking a lot, it's not like I haven't been working at all - plenty of time has gone into Courier, after all.

When I had this realisation yesterday, I tried my best to push myself to work on the game any way I could, but I failed to find anything that I could actually convince myself to do. That isn't to say that there aren't things to do; god forbid there's a tonne of things! Tuning combat, drawing animations, writing the UI, fixing dialogue, starting on enemy AI, optimising performance - just to name a few. And yet, despite the breadth and depth of things to do, there was absolutely nothing that looked appealing to me.

This kind of feeling is nothing new to me. It's a creative block, and happens more often that I'd like to admit. It's also why I often don't like to start long running projects, because I'm afraid of a creative block that would ruin it. The worst part about the creative block is that there's no remedy for it. You just get stuck in a rut, and it sucks a whole lot for a completely unpredictable amount of time. Often what I end up doing, whether consciously so or not, is switching to another project and just working on that.

So far that project has been Courier, but that's at its end and I'm also starting to feel burnt out on it, too. I don't have any other projects queued up that I'd like to tackle, or new ideas on what to do at the moment, so I'm just... stuck.

I suppose the right thing to do in this situation is to take it easy and not fret too much over it, since that's often one of the many factors causing the block. I've never been good at actually doing that, though. Maybe I should try to take a break from programming in general? I don't know.

You may be wondering why I'm writing this all to begin with. Well, partly I feel like I promised to do monthly and weekly updates, and I really hate to break that promise without notice. Another part is that I just feel like I owe you the discretion to tell you what's going on with me. I'm very thankful for the email replies and general responses I've gotten for Kandria so far, I really am! Because of that genuine interest, I feel all the more pressured not to disappoint. Since I have nothing to show though, I thought the only proper course of action is to just be open and direct about it. So I'll just say it again: aside from updating the public demo, no progress has been made at all.

Maybe it would help me to have a more open discussion about this topic in general, instead of just it being me telling you that I'm in a bad place. So please, let me know: have you been in similar situations before? What helped you deal with them? Is there something in Kandria I could try to focus on that you, personally, would like to see?

You can reach me at shinmera@tymoon.eu.




la

Marco Antoniotti: Digging CLAST

Again, after ELS 2020, I went back to double check the actual status of some of my libraries (after an embarrassing nag by Marco Heisig :) who caught me sleeping).

I updated the documentation of CLAST, and checked that its current status is ok; the only change I had to make was to conform to the latest ASDF expectations for test systems. Of course, you may find many more bugs.

CLAST is a library that produces abstract syntax trees munging Common Lisp sources. To do so, it relies on CLtL2 environments, which, as we all know, are in a sorry state in many implementations. Yet, CLAST is usable, at least for people who are ... CLAZY enough to use it.

(cheers)




la

Native Plants From Etsy

So, I ordered a bunch of native plants on Etsy, and they were WAY cheaper than they are in stores around here. They came as chunks of root, essentially. It only occurred to me after I ordered to worry about why they were so cheap — is it possible they’re being illegally harvested from woodlands? … Continue reading "Native Plants From Etsy"




la

Lamborghini Huracán EVO RWD Spyder




la

Lotus Elise Classic Heritage Edition




la

Glowforge Pro 3D Laser Printer














la

Osunlade Samples Dionne Warwick

musicisart magazine Osunlade Samples Dionne Warwick

Producers Osunlade sampled Dionne Warwick‘s classic R&B original Walk the Way You Talk. Osunlade turned their creation “Dionne” into a powerful message with an addictive, motivating dance beat. Released by Dionne Warwick in 1970, the phrasing sampled plays the words “Just because you say things are gonna change, saying something’s wrong isn’t good enough.” The meaning still […]

The post Osunlade Samples Dionne Warwick appeared first on musicisart magazine.









la

[tasty review] United Tastes of America by Gabrielle Langholtz, Jenny Bowers, and DL Acken

  Feeling a little peckish? What’s your pleasure? If you’re craving something savory, perhaps we should zip on over to Illinois for some deep dish pizza and pierogies. Something a little more substantial? Well, we could feast on chicken fried steak in Oklahoma and bison burgers in Wyoming, before topping everything off with a platter … Continue reading [tasty review] United Tastes of America by Gabrielle Langholtz, Jenny Bowers, and DL Acken




la

new art crush: jane newland

  I recently “discovered” UK illustrator Jane Newland while browsing images online. Safe to say that 80% of the time, when something different/exceptional/beautiful stops me in my tracks, the artist turns out to be British. ????         Jane lives and works in Norwich (the most complete Medieval city in the UK), which … Continue reading new art crush: jane newland




la

[review + recipe] On Wings of Words by Jennifer Berne and Becca Stadtlander

  Each bird, bee, blossom, butterfly — was a source of joy and wonder for young Emily Dickinson. In this beautiful new picture book biography, aptly illustrated with a butterfly motif, we witness her singular metamorphosis from a keenly observant child into one of the most original and innovative poets in American literature. On Wings … Continue reading [review + recipe] On Wings of Words by Jennifer Berne and Becca Stadtlander




la

The collapsed Maltese judicial system

It is obvious that the Maltese judicial system has totally collapsed. In todays The Times one can read of a man who has raped his nephew and niece and sexually abused their cousin when they were five, eight and thirteen years old. The abuses took place during several years until 2007. The father of the siblings reported this to the police 2007 and insisted that the police should take immediately action. The perpetrator, when then heard by the police, immediately admitted the acts and also showed the police videos that he previously had shown to his victims. The videos contained sexual actions the perpetrator had had with his wife. One can wonder why these terrible crimes not ended up in court until 2012! The man was this week sentenced to ten years in prison. What has happened since 2007? How have the victims and their families felt during this time? Is there any excuse for this failure of the judicial system? There is no wonder that the people in Malta has very low confidence in the judicial system and that so many people think that judges accept bribes; they are probably more interested in their own wellbeing than the one of people who have been abused. Those people are not abused only by a perpetrator but also by the judicial system. This is a shame on Malta and its (lack of ) functional judicial system.




la

Rule of law in Malta? Hardly.


You cannot call Malta a society where there is rule of law and where an individual's rights are protected. Malta must be one of Europe’s most unsafe countries if one is suspected of having committed a crime, especially if you are a foreigner (if you are black it is even worse). A Maltese is treated in a totally different way than a foreigner. For instance, it is very difficult to be granted bail for a foreigner even if you are an EU citizen. This is very strange since there is a treaty in EU which makes it easy to get an EU citizen extradited to another EU country. A member state can only deny another member state an extradition if  certain requirements are not fulfilled. Maybe Malta is afraid that it cannot fulfill such requirements and therefore prefers to keep a suspect in prison instead of granting him bail.  It is hard to even understand that Malta can be a member of EU. In Malta, you can be detained for an indefinite period. There is no limit whatsoever when the police must start a trial. Yes, in Malta it is the same person as investigates a case as brings it to court. Malta is like any dictatorship as far as protection of individual’s rights is concerned. Not even in the former Soviet Union you could be detained for indefinite time, if not a political crime. It was The Observer’s intention to vote today, but why should one vote in a country where there is no democracy but only hypocrisy. Malta is a sham democracy where individual rights seem to be less important than the rights of the state. You can read about this wonderful society in todays The Times.




la

Again, the Maltese judicial system is proven to have collapsed and now it also seems ridiculous



Today one can read in The Times of a man being sentenced to one month in prison and fined 233€ for illegal gambling. The fantastic and almost unbelievable fact is that the crime was committed in 2001 and the man pleaded guilty in 2002. The man had to wait ten years to be punished for a crime he had admitted almost immediately! To make this even more surprising (well, maybe not so surprising; this is probably typically for the judicial system in Malta) the judge found that the prosecution had failed to prove the allegations against the man, but, since he had admitted the crime the judge had to find him guilty. The Observer sincerely hopes that the latter is not true. In most other countries, with a more sophisticated and functioning judicial system than Malta, an admission is not enough to prove that a person has committed a crime.  When famous murders occur, quite many people come to the police and plead guilty. This is a well-known fact among Alphacriminologists. Probably and hopefully The Times has not published full details about why the judge had to find the man guilty.