4911afa16bebdd0278dc386f81ccebdc53c112a9
[8sync.git] / demos / actors / botherbotherbother.scm
1 #!/usr/bin/guile \
2 -e main -s
3 !#
4
5 ;;; 8sync --- Asynchronous programming for Guile
6 ;;; Copyright (C) 2016 Christopher Allan Webber <cwebber@dustycloud.org>
7 ;;;
8 ;;; This file is part of 8sync.
9 ;;;
10 ;;; 8sync is free software: you can redistribute it and/or modify it
11 ;;; under the terms of the GNU Lesser General Public License as
12 ;;; published by the Free Software Foundation, either version 3 of the
13 ;;; License, or (at your option) any later version.
14 ;;;
15 ;;; 8sync is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;;; GNU Lesser General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU Lesser General Public
21 ;;; License along with 8sync.  If not, see <http://www.gnu.org/licenses/>.
22
23 ;; Puppet show simulator.
24
25 (use-modules (8sync agenda)
26              (8sync actors)
27              (oop goops)
28              (ice-9 hash-table)
29              (ice-9 format))
30
31 (set! *random-state* (random-state-from-platform))
32 (define (random-choice lst)
33   (list-ref lst (random (length lst))))
34
35
36 (define student-names
37   '("Henry" "Harmony" "Rolf"))
38
39 (define (student-name-generator)
40   ;; a hashmap of student names with their current count
41   (define student-count (make-hash-table))
42   (lambda ()
43     (let* ((student (random-choice student-names))
44            (current-number (hash-ref student-count student 1)))
45       (hash-set! student-count student (1+ current-number))
46       (format #f "~a-~a" student current-number))))
47
48
49 (define-class <student> (<actor>)
50   (name #:init-keyword #:name)
51   (dead #:init-value #f
52         #:accessor student-dead)
53   (actions #:allocation #:each-subclass
54            #:init-value
55            (build-actions
56             (bother-professor
57              (lambda* (actor message #:key target)
58                "Go bother a professor"
59                (while (not (student-dead actor))
60                  (format #t "~a: Bother bother bother!\n"
61                          (actor-id-actor actor))
62                  (<- actor target
63                      'be-bothered
64                      #:noise "Bother bother bother!\n"))))
65
66             (be-lambda-consvardraed
67              (lambda (actor message)
68                "This kills the student."
69                (format #t "~a says: AAAAAAAHHHH!!! I'm dead!\n"
70                        (actor-id-actor actor))
71                (set! (student-dead actor) #t))))))
72
73 (define complaints
74   '("Hey!" "Stop that!" "Oof!"))
75
76 (define (professor-be-bothered actor message . rest)
77   (define whos-bothering (professor-bothered-by actor))
78   (hash-set! whos-bothering (message-from message) #t)
79
80   ;; Oof!  Those kids!
81   (display (string-append (random-choice complaints)))
82   (newline)
83
84   ;; More than one student is bothering us, lose our temper
85   (if (> (hash-count (const #t) whos-bothering)
86          1)
87       (begin
88         (format #t "~s: LAMBDA CONSVARDRA!\n"
89                 (actor-id actor))
90         (hash-for-each
91          (lambda (student _)
92            (<- actor student
93                'be-lambda-consvardraed)
94            ;; Remove student from bothering list
95            (hash-remove! whos-bothering student))
96          whos-bothering))
97       ;; Otherwise, remove them from the list and carry on
98       (hash-remove! whos-bothering (message-from message))))
99
100 (define-class <professor> (<actor>)
101   ;; This value checks whether any other actor is currently
102   ;; bothering this same character.
103   ;; We'll use a hash table as a fake set.
104   (bothered-by #:init-thunk make-hash-table
105                #:getter professor-bothered-by)
106   (actions #:allocation #:each-subclass
107            #:init-value
108            (build-actions
109             (be-bothered professor-be-bothered))))
110
111 (define num-students 10)
112
113 (define (main . args)
114   (define agenda (make-agenda))
115   (define hive (make-hive))
116   (define professor (hive-create-actor* hive <professor> "prof"))
117   (define namegen (student-name-generator))
118   (define students
119     (map
120      (lambda _
121        (let ((name (namegen)))
122          (hive-create-actor* hive <student> name
123                              #:name name)))
124      (iota num-students)))
125
126   ;; Bootstrap each student into bothering-professor mode.
127   (define start-bothering-tasks
128     (map
129      (lambda (student)
130        (bootstrap-message hive student 'bother-professor
131                                #:target professor))
132      students))
133
134   (run-hive hive start-bothering-tasks))