summaryrefslogtreecommitdiff
path: root/ipl/progs/snake.icn
blob: 60186eb4ed0523ca21bc8685f6b0d2a67ccb0f08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
############################################################################
#
#	File:     snake.icn
#
#	Subject:  Program to play the snake game
#
#	Author:   Richard L. Goerwitz
#
#	Date:     March 26, 2002
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#	Version:  1.9
#
############################################################################
#  
#      While away the idle moments watching the snake eat blank squares
#  on your screen.  Snake has only one (optional) argument -
#
#      usage:  snake [character]
#
#  where "character" represents a single character to be used in drawing
#  the snake.  The default is an "o."  In order to run snake, your ter-
#  minal must have cursor movement capability, and must be able to do re-
#  verse video.
#
#      I wrote this program to test itlib.icn, iscreen.icn, and some
#  miscellaneous utilities I wrote.  It clears the screen, moves the cur-
#  sor to arbitrary squares on the screen, changes video mode, and in
#  general exercises the terminal capability database on the target ma-
#  chine.
#
############################################################################
#
#  Bugs:  Most magic cookie terminals just won't work.  Terminal really
#  needs reverse video (it will work without, but won't look as cute).
#
############################################################################
#
#  Requires:  UNIX (MS-DOS is okay, if you replace itlib with itlibdos.icn)
#
############################################################################
#
#  Links: itlib, iscreen, random
#
############################################################################

link itlib
link iscreen
link random

global max_l, max_w, snake_char

record wholething(poop,body)

procedure main(a)

    local snake, limit, sl, sw, CM, x, r, leftbehind

    randomize()

    if not (getval("so"),  CM := getval("cm"))
    then stop("snake:  Your terminal is too stupid to run me.  Sorry.")
    clear(); Kludge() # if your term likes it, use emphasize(); clear()
    # Decide how much space we have to operate in.
    max_l := getval("li")-2             # global
    max_w := getval("co")-1             # global
    # Determine the character that will be used to represent the snake.
    snake_char := (\a[1])[1] | "o"

    # Make the head.
    snake := []; put(snake,[?(max_l-1)+1, ?(max_w-1)+1])
    # Make the body, displaying it as it grows.
    every x := 2 to 25 do {
	display(,snake)
	put(snake,findnext(snake[x-1],snake))
    }

    # Begin "eating" all the standout mode spaces on the screen.
    repeat {
	r := makenew(snake)
	leftbehind := r.poop
	snake := r.body
	display(leftbehind,snake) | break
    }

    # Shrink the snake down to nothing, displaying successively smaller bits.
    while leftbehind := get(snake)
    do display(leftbehind,snake)

    iputs(igoto(getval("cm"), 1, getval("li")-1))
    normal()
    
end 



procedure findnext(L, snake)

    local i, j, k, op, l
    static sub_lists
    initial {
	sub_lists := [[1,2,3], [1,3,2], [3,2,1], [3,1,2], [2,1,3], [2,3,1]]
    }
    # global max_l, max_w

    i := L[1]; j := L[2]	# for clarity, use i, j (not l[i|j])

    # L is the last snake segment; find k and l, such that k and l are
    # valid line and column numbers differing from l[1] and l[2] by no
    # more than 1, respectively.  Put simply:  Create a new segment
    # [k, l] adjacent to the last one (L).

    op := (different | Null) &
    (k := max_l+1 > [i,i+1,i-1][!sub_lists[?6]]) > 1 &
	(l := max_w+1 > [j,j+1,j-1][!sub_lists[?6]]) > 1 &
	op([k, l], snake)

    return [k, l]

end



procedure different(l,snake)

    local bit
    (l[1] = (bit := !\snake)[1], l[2] = bit[2]) & fail
    return

end



procedure Null(a[])
    return
end



procedure display(lb,snake)

    local last_segment, character
    static CM
    initial CM := getval("cm")

    # Change the mode of the square just "vacated" by the moving snake.
    if *snake = 0 | different(\lb,snake) then {
	iputs(igoto(CM, lb[2], lb[1]))
	normal()
	writes(" ")
    }

    if last_segment := (0 ~= *snake) then {
	# Write the last segment (which turns out to be the snakes head!).
	iputs(igoto(CM, snake[last_segment][2], snake[last_segment][1]))
	emphasize(); writes(snake_char)  # snake_char is global
    }

    # Check to see whether we've eaten every edible square on the screen.
    if done_yet(lb)
    then fail
    else return

end



procedure makenew(snake)
    local leftbehind, i

    # Move each constituent list up one position in snake, discard
    # the first element, and tack a new one onto the end.

    every i := 1 to *snake - 1 do
	snake[i] :=: snake[i+1]
    leftbehind := copy(snake[i+1])
    snake[i+1] := findnext(snake[i],snake)
    return wholething(leftbehind,snake)
    
end



procedure the_same(l1, l2)

    if l1[1] = l2[1] & l1[2] = l2[2]
    then return else fail

end



procedure done_yet(l)
    local i, j

    # Check to see if we've eaten every edible square on the screen.
    # It's easy for snake to screw up on this one, since somewhere
    # along the line most terminal/driver/line combinations will con-
    # spire to drop a character somewhere along the line.

    static square_set
    initial {

	square_set := set()
	every i := 2 to max_l do {
	    every j := 2 to max_w do {
		insert(square_set, i*j)
	    }
	}
    }

    /l & fail
    delete(square_set, l[1]*l[2])
    if *square_set = 0 then return
    else fail

end



procedure Kludge()
    local i

    # Horrible way of clearing the screen to all reverse-video, but
    # the only apparent way we can do it "portably" using the termcap
    # capability database.

    iputs(igoto(getval("cm"),1,1))
    if getval("am") then {
	emphasize()
        every 1 to (getval("li")-1) * getval("co") do
	    writes(" ")
    }
    else {
	every i := 1 to getval("li")-1 do {
	    iputs(igoto(getval("cm"), 1, i))
	    emphasize()
	    writes(repl(" ",getval("co")))
	}
    }
    iputs(igoto(getval("cm"),1,1))

end