View difference between Paste ID: Yq1nJf4K and 08thyHVw
SHOW: | | - or go back to the newest paste.
1
-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe
2
--
3
-- SPDX-License-Identifier: LicenseRef-CCPL
4
5
--[[
6
Falling - Based on Tetris by Alexey Pajitnov
7
This version written by Gopher, at the request of Dan200, for
8
ComputerCraft v1.6. No particular rights are reserved.
9
--]]
10
11
local function colorass(c, bw)
12
  return term.isColor() and c or bw
13
end
14
15
local block_s1 = {
16
    {
17
      { 1, 0, 0, 0 },
18
      { 1, 1, 0, 0 },
19
      { 0, 1, 0, 0 },
20
      { 0, 0, 0, 0 },
21
    },
22
    {
23
      { 0, 0, 0, 0 },
24
      { 0, 1, 1, 0 },
25
      { 1, 1, 0, 0 },
26
      { 0, 0, 0, 0 },
27
    },
28
    ch = colorass("  ", "{}"),
29
    fg = colorass(colors.blue, colors.black),
30
    bg = colorass(colors.cyan, colors.white),
31
  }
32
local block_s2 = {
33
    {
34
      { 0, 1, 0, 0 },
35
      { 1, 1, 0, 0 },
36
      { 1, 0, 0, 0 },
37
      { 0, 0, 0, 0 },
38
    },
39
    {
40
      { 0, 0, 0, 0 },
41
      { 1, 1, 0, 0 },
42
      { 0, 1, 1, 0 },
43
      { 0, 0, 0, 0 },
44
    },
45
    ch = colorass("  ", "{}"),
46
    fg = colorass(colors.green, colors.black),
47
    bg = colorass(colors.lime, colors.white),
48
  }
49
local block_line = {
50
    {
51
      { 0, 1, 0, 0 },
52
      { 0, 1, 0, 0 },
53
      { 0, 1, 0, 0 },
54
      { 0, 1, 0, 0 },
55
    },
56
    {
57
      { 0, 0, 0, 0 },
58
      { 1, 1, 1, 1 },
59
      { 0, 0, 0, 0 },
60
      { 0, 0, 0, 0 },
61
    },
62
    ch = colorass("  ", "[]"),
63
    fg = colorass(colors.pink, colors.black),
64
    bg = colorass(colors.red, colors.white),
65
  }
66
local block_square = {
67
    {
68
      { 1, 1, 0, 0 },
69
      { 1, 1, 0, 0 },
70
      { 0, 0, 0, 0 },
71
      { 0, 0, 0, 0 },
72
    },
73
    ch = colorass("  ", "[]"),
74
    fg = colorass(colors.lightBlue, colors.black),
75
    bg = colorass(colors.blue, colors.white),
76
  }
77
local block_L1 = {
78
    {
79
      { 1, 1, 0, 0 },
80
      { 0, 1, 0, 0 },
81
      { 0, 1, 0, 0 },
82
      { 0, 0, 0, 0 },
83
    },
84
    {
85
      { 0, 0, 0, 0 },
86
      { 1, 1, 1, 0 },
87
      { 1, 0, 0, 0 },
88
      { 0, 0, 0, 0 },
89
    },
90
    {
91
      { 0, 1, 0, 0 },
92
      { 0, 1, 0, 0 },
93
      { 0, 1, 1, 0 },
94
      { 0, 0, 0, 0 },
95
    },
96
    {
97
      { 0, 0, 1, 0 },
98
      { 1, 1, 1, 0 },
99
      { 0, 0, 0, 0 },
100
      { 0, 0, 0, 0 },
101
    },
102
    ch = colorass("  ", "()"),
103
    fg = colorass(colors.orange, colors.black),
104
    bg = colorass(colors.yellow, colors.white),
105
  }
106
local block_L2 = {
107
    {
108
      { 0, 1, 0, 0 },
109
      { 0, 1, 0, 0 },
110
      { 1, 1, 0, 0 },
111
      { 0, 0, 0, 0 },
112
    },
113
    {
114
      { 0, 0, 0, 0 },
115
      { 1, 1, 1, 0 },
116
      { 0, 0, 1, 0 },
117
      { 0, 0, 0, 0 },
118
    },
119
    {
120
      { 0, 1, 1, 0 },
121
      { 0, 1, 0, 0 },
122
      { 0, 1, 0, 0 },
123
      { 0, 0, 0, 0 },
124
    },
125
    {
126
      { 1, 0, 0, 0 },
127
      { 1, 1, 1, 0 },
128
      { 0, 0, 0, 0 },
129
      { 0, 0, 0, 0 },
130
    },
131
    ch = colorass("  ", "()"),
132
    fg = colorass(colors.brown, colors.black),
133
    bg = colorass(colors.orange, colors.white),
134
  }
135
local block_T = {
136
    {
137
      { 0, 1, 0, 0 },
138
      { 1, 1, 0, 0 },
139
      { 0, 1, 0, 0 },
140
      { 0, 0, 0, 0 },
141
    },
142
    {
143
      { 0, 0, 0, 0 },
144
      { 1, 1, 1, 0 },
145
      { 0, 1, 0, 0 },
146
      { 0, 0, 0, 0 },
147
    },
148
    {
149
      { 0, 1, 0, 0 },
150
      { 0, 1, 1, 0 },
151
      { 0, 1, 0, 0 },
152
      { 0, 0, 0, 0 },
153
    },
154
    {
155
      { 0, 1, 0, 0 },
156
      { 1, 1, 1, 0 },
157
      { 0, 0, 0, 0 },
158
      { 0, 0, 0, 0 },
159
    },
160
    ch = colorass("  ", "<>"),
161
    fg = colorass(colors.cyan, colors.black),
162
    bg = colorass(colors.purple, colors.white),
163
  }
164
165
local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T }
166
167
local points = { 4, 10, 30, 120 }
168
169
local function lpad(text, amt)
170
  text = tostring(text)
171
  return string.rep(" ", amt - #text) .. text
172
end
173
174
local width, height = term.getSize()
175
176
if height < 19 or width < 26 then
177
  print("Your screen is too small to play :(")
178
  return
179
end
180
181
182
local speedsByLevel = {
183
  1.2,
184
  1.0,
185
   .8,
186
   .65,
187
   .5,
188
   .4,
189
   .3,
190
   .25,
191
   .2,
192
   .15,
193
   .1,
194
   .05, }
195
196
local level = 1
197
198
local function playGame()
199
  local score = 0
200
  local lines = 0
201
  local initialLevel = level
202
  local next = blocks[math.random(1, #blocks)]
203
204
  local pit = {}
205
206
207
  local heightAdjust = 0
208
209
  if height <= 19 then
210
    heightAdjust = 1
211
  end
212
213
214
215
  local function drawScreen()
216
    term.setTextColor(colors.white)
217
    term.setBackgroundColor(colors.black)
218
    term.clear()
219
220
    term.setTextColor(colors.black)
221
    term.setBackgroundColor(colorass(colors.lightGray, colors.white))
222
    term.setCursorPos(22, 2)
223
    term.write("Score") --score
224
    term.setCursorPos(22, 5)
225
    term.write("Level")  --level
226
    term.setCursorPos(22, 8)
227
    term.write("Lines")  --lines
228
    term.setCursorPos(22, 12)
229
    term.write("Next") --next
230
231
    term.setCursorPos(21, 1)
232
    term.write("      ")
233
    term.setCursorPos(21, 2)
234
    term.write(" ") --score
235
    term.setCursorPos(21, 3)
236
    term.write(" ")
237
    term.setCursorPos(21, 4)
238
    term.write("      ")
239
    term.setCursorPos(21, 5)
240
    term.write(" ")  --level
241
    term.setCursorPos(21, 6)
242
    term.write(" ")
243
    term.setCursorPos(21, 7)
244
    term.write("      ")
245
    term.setCursorPos(21, 8)
246
    term.write(" ")  --lines
247
    term.setCursorPos(21, 9)
248
    term.write(" ")
249
    term.setCursorPos(21, 10)
250
    term.write("      ")
251
    term.setCursorPos(21, 11)
252
    term.write("      ")
253
    term.setCursorPos(21, 12)
254
    term.write(" ") --next
255
    term.setCursorPos(26, 12)
256
    term.write(" ") --next
257
    term.setCursorPos(21, 13)
258
    term.write("      ")
259
    term.setCursorPos(21, 14)
260
    term.write(" ")
261
    term.setCursorPos(21, 15)
262
    term.write(" ")
263
    term.setCursorPos(21, 16)
264
    term.write(" ")
265
    term.setCursorPos(21, 17)
266
    term.write(" ")
267
    term.setCursorPos(21, 18)
268
    term.write(" ")
269
    term.setCursorPos(21, 19)
270
    term.write("      ")
271
    term.setCursorPos(21, 20)
272
    term.write("      ")
273
  end
274
275
  local function updateNumbers()
276
    term.setTextColor(colors.white)
277
    term.setBackgroundColor(colors.black)
278
279
    term.setCursorPos(22, 3)
280
    term.write(lpad(score, 5)) --score
281
    term.setCursorPos(22, 6)
282
    term.write(lpad(level, 5))  --level
283
    term.setCursorPos(22, 9)
284
    term.write(lpad(lines, 5))  --lines
285
  end
286
287
  local function drawBlockAt(block, xp, yp, rot)
288
    term.setTextColor(block.fg)
289
    term.setBackgroundColor(block.bg)
290
    for y = 1, 4 do
291
      for x = 1, 4 do
292
        if block[rot][y][x] == 1 then
293
          term.setCursorPos((xp + x) * 2 - 3, yp + y - 1 - heightAdjust)
294
          term.write(block.ch)
295
        end
296
      end
297
    end
298
  end
299
300
  local function eraseBlockAt(block, xp, yp, rot)
301
    term.setTextColor(colors.white)
302
    term.setBackgroundColor(colors.black)
303
    for y = 1, 4 do
304
      for x = 1, 4 do
305
        if block[rot][y][x] == 1 then
306
          term.setCursorPos((xp + x) * 2 - 3, yp + y - 1 - heightAdjust)
307
          term.write("  ")
308
        end
309
      end
310
    end
311
  end
312
313
  local function testBlockAt(block, xp, yp, rot)
314
    for y = 1, 4 do
315
      local ty = yp + y - 1
316
      for x = 1, 4 do
317
        local tx = xp + x - 1
318
        if block[rot][y][x] == 1 then
319
          if tx > 10 or tx < 1 or ty > 20 or pit[ty][tx] ~= 0 then
320
            return true
321
          end
322
        end
323
      end
324
    end
325
  end
326
327
  local function pitBlock(block, xp, yp, rot)
328
    for y = 1, 4 do
329
      for x = 1, 4 do
330
        if block[rot][y][x] == 1 then
331
          pit[yp + y - 1][xp + x - 1] = block
332
        end
333
      end
334
    end
335
  end
336
337
338
  local function clearPit()
339
    for row = 1, 20 do
340
      pit[row] = {}
341
      for col = 1, 10 do
342
        pit[row][col] = 0
343
      end
344
    end
345
  end
346
347
348
349
  drawScreen()
350
  updateNumbers()
351
352
  --declare & init the pit
353
  clearPit()
354
355
356
357
  local halt = false
358
  local dropSpeed = speedsByLevel[math.min(level, 12)]
359
360
361
  local curBlock = next
362
  next = blocks[math.random(1, 7)]
363
364
  local curX, curY, curRot = 4, 1, 1
365
  local dropTimer = os.startTimer(dropSpeed)
366
367
  drawBlockAt(next, 11.5, 15 + heightAdjust, 1)
368
  drawBlockAt(curBlock, curX, curY, curRot)
369
370
  local function redrawPit()
371
    for r = 1 + heightAdjust, 20 do
372
      term.setCursorPos(1, r - heightAdjust)
373
      for c = 1, 10 do
374
        if pit[r][c] == 0 then
375
          term.setTextColor(colors.black)
376
          term.setBackgroundColor(colors.black)
377
          term.write("  ")
378
        else
379
          term.setTextColor(pit[r][c].fg)
380
          term.setBackgroundColor(pit[r][c].bg)
381
          term.write(pit[r][c].ch)
382
        end
383
      end
384
    end
385
  end
386
387
  local function hidePit()
388
    for r = 1 + heightAdjust, 20 do
389
      term.setCursorPos(1, r - heightAdjust)
390
      term.setTextColor(colors.black)
391
      term.setBackgroundColor(colors.black)
392
      term.write("                    ")
393
    end
394
  end
395
396
  local function msgBox(message)
397
    local x = math.floor((17 - #message) / 2)
398
    term.setBackgroundColor(colorass(colors.lightGray, colors.white))
399
    term.setTextColor(colors.black)
400
    term.setCursorPos(x, 9)
401
    term.write("+" .. string.rep("-", #message + 2) .. "+")
402
    term.setCursorPos(x, 10)
403
    term.write("|")
404
    term.setCursorPos(x + #message + 3, 10)
405
    term.write("|")
406
    term.setCursorPos(x, 11)
407
    term.write("+" .. string.rep("-", #message + 2) .. "+")
408
    term.setTextColor(colors.white)
409
    term.setBackgroundColor(colors.black)
410
    term.setCursorPos(x + 1, 10)
411
    term.write(" " .. message .. " ")
412
  end
413
414
  local function clearRows()
415
    local rows = {}
416
    for r = 1, 20 do
417
      local count = 0
418
      for c = 1, 10 do
419
        if pit[r][c] ~= 0 then
420
          count = count + 1
421
        else
422
          break
423
        end
424
      end
425
      if count == 10 then
426
        rows[#rows + 1] = r
427
      end
428
    end
429
430
    if #rows > 0 then
431
      for _ = 1, 4 do
432
        sleep(.1)
433
        for r = 1, #rows do
434
          r = rows[r]
435
          term.setCursorPos(1, r - heightAdjust)
436
          for c = 1, 10 do
437
            term.setTextColor(pit[r][c].bg)
438
            term.setBackgroundColor(pit[r][c].fg)
439
            term.write(pit[r][c].ch)
440
          end
441
        end
442
        sleep(.1)
443
        for r = 1, #rows do
444
          r = rows[r]
445
          term.setCursorPos(1, r - heightAdjust)
446
          for c = 1, 10 do
447
            term.setTextColor(pit[r][c].fg)
448
            term.setBackgroundColor(pit[r][c].bg)
449
            term.write(pit[r][c].ch)
450
          end
451
        end
452
      end
453
      --now remove the rows and drop everything else
454
      term.setBackgroundColor(colors.black)
455
      for r = 1, #rows do
456
        r = rows[r]
457
        term.setCursorPos(1, r - heightAdjust)
458
        term.write("                    ")
459
      end
460
      sleep(.25)
461
      for r = 1, #rows do
462
        table.remove(pit, rows[r])
463
        table.insert(pit, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })
464
      end
465
      redrawPit()
466
      lines = lines + #rows
467
      score = score + points[#rows] * math.min(level, 20)
468
      level = math.floor(lines / 10) + initialLevel
469
      dropSpeed = speedsByLevel[math.min(level, 12)]
470
      updateNumbers()
471
    end
472
    sleep(.25)
473
  end
474
475
  local function blockFall()
476
    if testBlockAt(curBlock, curX, curY + 1, curRot) then
477
      pitBlock(curBlock, curX, curY, curRot)
478
      --detect rows that clear
479
      clearRows()
480
481
      curBlock = next
482
      curX = 4
483
      curY = 1
484
      curRot = 1
485
      if testBlockAt(curBlock, curX, curY, curRot) then
486
        halt = true
487
      end
488
      drawBlockAt(curBlock, curX, curY, curRot)
489
      eraseBlockAt(next, 11.5, 15 + heightAdjust, 1)
490
      next = blocks[math.random(1, 7)]
491
      drawBlockAt(next, 11.5, 15 + heightAdjust, 1)
492
      return true
493
    else
494
      eraseBlockAt(curBlock, curX, curY, curRot)
495
      curY = curY + 1
496
      drawBlockAt(curBlock, curX, curY, curRot)
497
      return false
498
    end
499
  end
500
501
502
  while not halt do
503
    local e = { os.pullEvent() }
504
    if e[1] == "timer" then
505
      if e[2] == dropTimer then
506
        blockFall()
507
        dropTimer = os.startTimer(dropSpeed)
508
      end
509
    elseif e[1] == "key" then
510
      local key = e[2]
511
      local dx, dy, dr = 0, 0, 0
512
      if key == keys.left or key == keys.a then
513
        dx = -1
514
      elseif key == keys.right or key == keys.d then
515
        dx = 1
516
      elseif key == keys.up or key == keys.w then
517
        dr = 1
518
      elseif key == keys.down or key == keys.s then
519
        while not blockFall() do end
520
        dropTimer = os.startTimer(dropSpeed)
521
      elseif key == keys.space then
522
        hidePit()
523
        msgBox("Paused")
524
        while ({ os.pullEvent("key") })[2] ~= keys.space do end
525
        redrawPit()
526
        drawBlockAt(curBlock, curX, curY, curRot)
527
        dropTimer = os.startTimer(dropSpeed)
528
      end
529
      if dx + dr ~= 0 then
530
        if not testBlockAt(curBlock, curX + dx, curY + dy, dr > 0 and curRot % #curBlock + dr or curRot) then
531
          eraseBlockAt(curBlock, curX, curY, curRot)
532
          curX = curX + dx
533
          curY = curY + dy
534
          curRot = dr == 0 and curRot or curRot % #curBlock + dr
535
          drawBlockAt(curBlock, curX, curY, curRot)
536
        end
537
      end
538
    elseif e[1] == "term_resize" then
539
      local _, h = term.getSize()
540
      if h == 20 then
541
        heightAdjust = 0
542
      else
543
        heightAdjust = 1
544
      end
545
      redrawPit()
546
      drawBlockAt(curBlock, curX, curY, curRot)
547
    end
548
  end
549
550
  msgBox("Game Over!")
551
  while true do
552
    local _, k = os.pullEvent("key")
553
    if k == keys.space or k == keys.enter or k == keys.numPadEnter then
554
      break
555
    end
556
  end
557
558
  level = math.min(level, 9)
559
end
560
561
562
local selected = 1
563
local playersDetected = false
564
565
local function drawMenu()
566
  term.setBackgroundColor(colors.black)
567
  term.setTextColor(colorass(colors.red, colors.white))
568
  term.clear()
569
570
  local cx, cy = math.floor(width / 2), math.floor(height / 2)
571
572
  term.setCursorPos(cx - 6, cy - 2)
573
  term.write("F A L L I N G")
574
575
  if playersDetected then
576
    if selected == 0 then
577
      term.setTextColor(colorass(colors.blue, colors.black))
578
      term.setBackgroundColor(colorass(colors.gray, colors.white))
579
    else
580
      term.setTextColor(colorass(colors.lightBlue, colors.white))
581
      term.setBackgroundColor(colors.black)
582
    end
583
    term.setCursorPos(cx - 12, cy)
584
    term.write(" Play head-to-head game! ")
585
  end
586
587
  term.setCursorPos(cx - 10, cy + 1)
588
  if selected == 1 then
589
    term.setTextColor(colorass(colors.blue, colors.black))
590
    term.setBackgroundColor(colorass(colors.lightGray, colors.white))
591
  else
592
    term.setTextColor(colorass(colors.lightBlue, colors.white))
593
    term.setBackgroundColor(colors.black)
594
  end
595
  term.write(" Play from level: <" .. level .. "> ")
596
597
  term.setCursorPos(cx - 3, cy + 3)
598
  if selected == 2 then
599
    term.setTextColor(colorass(colors.blue, colors.black))
600
    term.setBackgroundColor(colorass(colors.lightGray, colors.white))
601
  else
602
    term.setTextColor(colorass(colors.lightBlue, colors.white))
603
    term.setBackgroundColor(colors.black)
604
  end
605
  term.write(" Quit ")
606
end
607
608
609
local function runMenu()
610
  drawMenu()
611
612
  while true do
613
    local event = { os.pullEvent() }
614
    if event[1] == "key" then
615
      local key = event[2]
616
      if key == keys.right or key == keys.d and selected == 1 then
617
        level = math.min(level + 1, 9)
618
        drawMenu()
619
      elseif key == keys.left or key == keys.a and selected == 1 then
620
        level = math.max(level - 1, 1)
621
        drawMenu()
622
      elseif key >= keys.one and key <= keys.nine and selected == 1 then
623
        level = key - keys.one + 1
624
        drawMenu()
625
      elseif key == keys.up or key == keys.w then
626
        selected = selected - 1
627
        if selected == 0 then
628
          selected = 2
629
        end
630
        drawMenu()
631
      elseif key == keys.down or key == keys.s then
632
        selected = selected % 2 + 1
633
        drawMenu()
634
      elseif key == keys.enter or key == keys.numPadEnter or key == keys.space then
635
        break --begin play!
636
      end
637
    end
638
  end
639
end
640
641
while true do
642
  runMenu()
643
  if selected == 2 then
644
    break
645
  end
646
647
  playGame()
648
end
649
650
651
term.setTextColor(colors.white)
652
term.setBackgroundColor(colors.black)
653
term.clear()
654
term.setCursorPos(1, 1)
655