-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtetris-with-p5js-and-chatgpt-tutorial.html
1380 lines (1243 loc) · 76.2 KB
/
tetris-with-p5js-and-chatgpt-tutorial.html
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--
https://jakedowns.github.io/starpages/tetris-with-p5js-and-chatgpt-tutorial.html
https://github.com/jakedowns/starpages/blob/main/tetris-with-p5js-and-chatgpt-tutorial.html
-->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>approaching behavior driven development (BDD) by making (popular block sorting game) with processing p5.js and ChatGPT - Test Driven Development (TDD) - Free Tutorials - Gherkin Syntax</title>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-tomorrow.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-javascript.min.js"></script>
<style>
.text-white {
color: #fff;
}
/* Additional CSS */
.menu-button {
display: none;
}
@media (min-width: 768px) {
.desktop-menu {
display: none;
}
.menu-button {
display: block;
}
/* Adjust layout for tablet landscape/desktop */
.layout {
display: flex;
flex-direction: row;
}
.menu-sections {
flex-basis: 30%;
position: fixed;
max-width: 300px
}
.content-viewer {
min-width: 100px;
}
.content-viewer {
flex-basis: 70%;
margin-left: 1rem;
}
#contentViewerList {
margin-top: calc(50% - 33%);
}
#contentViewerList {
margin-top: calc(50% - 33%);
}
.content-viewer-outer {
max-height: 100vh;
overflow-y: auto;
min-width: calc( 100vw - 400px);
}
/* aka #contentViewerList */
.content-viewer-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-auto-rows: minmax(100px, auto);
grid-gap: 1rem;
grid-auto-flow: dense;
align-items: start;
}
}
@media (max-width: 767px) {
.mobile-menu {
display: none;
}
}
/* Dark theme styling */
body {
background-color: #111827;
/* Tailwind color: bg-gray-900 */
}
.menu-section {
background-color: #4B5563;
/* Tailwind color: bg-gray-600 */
color: #F9FAFB;
/* Tailwind color: text-gray-100 */
}
.menu-section:hover {
background-color: #6B7280;
/* Tailwind color: bg-gray-500 */
}
.active-section {
background-color: #6D28D9;
/* Tailwind color: bg-purple-700 */
}
.title {
color: #F9FAFB;
/* Tailwind color: text-gray-100 */
}
.mobile-menu-button {
color: #F9FAFB;
/* Tailwind color: text-gray-100 */
}
.mobile-menu-close {
color: #EF4444;
/* Tailwind color: text-red-500 */
}
/* Content Viewer */
.content-viewer {
background-color: #1F2937;
/* Tailwind color: bg-gray-800 */
color: #F9FAFB;
/* Tailwind color: text-gray-100 */
padding: 2rem;
margin-top: 1rem;
border-radius: 0.5rem;
}
.header-image-wrapper {
cursor: pointer;
h2 {
text-align: center;
padding: 20px;
}
border-top: 1px solid #fff;
border-right: 1px solid #666;
border-bottom: 1px solid #333;
border-left: 1px solid #666;
border-radius: 0.5rem;
img {
border-radius: 0.5rem;
opacity: 0.1;
transition: all 0.3s ease;
filter: blur(5px);
}
}
.content-viewer:hover .header-image-wrapper {
img {
opacity: 0.5;
filter: blur(0px);
}
}
:root {
--max-columns: 4;
}
.grid-container {
display: grid;
grid-template-columns: repeat(var(--max-columns), 1fr);
}
.content-viewer {
transition: all 0.3s ease;
}
body:not(.force-to-one-col) .content-viewer {
transition: all 1s ease;
max-height: 100px;
overflow: hidden;
}
body.force-to-one-col .grid-container {
display: flex !important;
flex-direction: column !important;
flex-wrap: wrap !important;
}
body.force-to-one-col .content-viewer {
/* min-width: calc(100vw - 800px) !important;
max-width: calc(100vw - 800px) !important;
flex-basis: calc(100vw - 800px) !important;
width: calc(100vw - 800px) !important; */
}
.bg-midnightblue {
background-color: #191970;
}
.text-palegoldenrod {
color: #eee8aa;
}
.credits {
max-height: none !important;
}
.credits a {
font-weight: bold;
text-decoration: underline;
}
:root {
--bg-image-in: url('./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp');
--bg-image-out: url('./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp');
--bg-image: var(--bg-image-in);
}
.bg-img {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: var(--bg-image);
background-size: cover;
background-position: center center;
transition: opacity 1s ease, transform 1s ease;
/* transform: translateY(-100px); */
opacity: 0.1;
z-index: -1;
}
.bg-img::before,
.bg-img::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp');
background-size: cover;
background-position: center center;
transition: opacity 1s ease, transform 1s ease;
}
.bg-img::before {
background-image: var(--bg-image-out);
opacity: 1;
animation: fadeOut 0.3s ease forwards;
}
.bg-img::after {
background-image: var(--bg-image-in);
opacity: 0;
animation: fadeIn 0.3s ease forwards;
z-index: -1;
}
.bg-img.swap::before {
animation-name: fadeOutUp;
}
.bg-img.swap::after {
animation-name: fadeInUp;
}
:root {
--translate-amount: 100px;
--neg-translate-amount: calc(-1 * var(--translate-amount));
}
@keyframes fadeIn {
0% { opacity: 0; transform: translateY(var(--translate-amount)); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes fadeOut {
0% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(calc(-1 * var(--translate-amount))); }
}
@keyframes fadeInUp {
0% { opacity: 0; transform: translateY(var(--translate-amount)); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes fadeOutUp {
0% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(var(--neg-translate-amount)); }
}
.bg-img.show::before {
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
transform: translateY(-10px);
z-index: -1;
}
.menu-section#nav-intro::before {
background-image: url('/res/DALL·E 2023-12-26 15.57.30 - A visually compelling blog header image, designed in dark mode, that encapsulates the themes of artificial intelligence, programming, and Tetris, with.png');
}
.bg-img.show::after {
opacity: 1;
}
.timeline-wrapper {
/* position: absolute; */
height: 390px;
width: 2px;
}
.timeline-bg {
/* top: 12px; */
top: -8px;
width: 10px;
height: 390px;
background-color: #ddd;
position: absolute;
z-index: 1;
}
.timeline_covered {
top: -8px;
width: 10px;
height: 25%;
z-index: 2;
background-color: #6D28D9;
position: absolute;
transition: height 0.3s ease;
}
.timeline-circle {
z-index: 3;
position: relative;
height: 25px;
width: 25px;
/* margin-top: 10px; */
border-radius: 50%;
background-color: #ddd;
/* margin-bottom: 10px; */
/* top: 6.25px; */
margin: 23.5px 0;
left: -8px;
top: -20px;
box-sizing: border-box;
transition: background-color 0.15s ease 0.15s;
}
.timeline-circle.visited {
background-color: #6D28D9;
}
/* at DESKTOP resolution, force #contentViewerList to flex row with 3 columns */
@media (min-width: 768px) {
#contentViewerList {
overflow-x: hidden;
/* display: flex;
flex-direction: row;
flex-wrap: wrap; nowrap; */
/* justify-content: flex-start; */
/* align-items: stretch; */
/* align-content: stretch; */
}
#contentViewerList .content-viewer {
/*flex-basis: calc(33% - 50px);*/
}
}
.header-image-wrapper {
cursor: pointer;
h2 {
text-align: center;
padding: 20px;
}
border-top: 1px solid #fff;
border-right: 1px solid #666;
border-bottom: 1px solid #333;
border-left: 1px solid #666;
border-radius: 0.5rem;
img {
border-radius: 0.5rem;
opacity: 0.1;
transition: all 0.3s ease;
filter: blur(5px);
}
}
.content-viewer {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 2px 0 rgba(0, 0, 0, 0.12);
}
.content-viewer:hover {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.32), 0 2px 10px 0 rgba(0, 0, 0, 0.24);
}
.content-viewer:hover .header-image-wrapper {
img {
opacity: 0.5;
filter: blur(0px);
}
}
:root {
--max-columns: 4;
}
.grid-container {
display: grid;
grid-template-columns: repeat(var(--max-columns), 1fr);
}
.content-viewer {
transition: all 0.3s ease;
}
body:not(.force-to-one-col) .content-viewer {
transition: max-height 0.3s ease;
max-height: 100px;
overflow: hidden;
}
body.force-to-one-col .grid-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
body.force-to-one-col .content-viewer {
width: 100% !important;
flex-basis: 100% !important;
}
</style>
</head>
<body class="font-sans">
<div class="bg-img show"></div>
<!-- Combined Layout -->
<div class="layout">
<!-- Menu Sections -->
<div class="menu-sections flex flex-col items-start p-8">
<div class="text-2xl font-bold mb-4 title cursor-pointer" onclick="switchContent('intro')">learning BDD by building tetris with p5.js and chatGPT</div>
<div class="text-l font-semibold mb-2 text-white">A step by step guide using Behavior Driven Development focused prompting</div>
<div class="flex flex-row flex-wrap">
<div class="flex flex-row w-full">
<button onclick="switchContent('results');"
class="bg-green-500 hover:bg-green-700 text-white font-bold p-4 m-4 rounded w-1/2">Play</button>
<button onclick="scrollToPreviousSection();"
class="w-1/2 bg-red-500 hover:bg-red-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button onclick="scrollToNextSection();"
class="w-1/2 bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
<div class="relative">
<div class="timeline-wrapper relative block">
<div class="timeline-bg"></div>
<div class="timeline_covered"></div>
<div class="timeline">
<div class="timeline-inner relative block">
<div id="timeline-intro" class="timeline-circle visited"></div>
<div id="timeline-terms" class="timeline-circle"></div>
<div id="timeline-building" class="timeline-circle"></div>
<div id="timeline-tetrominos" class="timeline-circle"></div>
<div id="timeline-game" class="timeline-circle"></div>
<div id="timeline-controls" class="timeline-circle"></div>
<div id="timeline-mobile" class="timeline-circle"></div>
<div id="timeline-results" class="timeline-circle"></div>
<div id="timeline-future" class="timeline-circle"></div>
<div id="timeline-credits" class="timeline-circle"></div>
</div>
</div>
</div>
<div class="space-y-2 pl-8 absolute" style="left: 100%; right: 0; top: 0;">
<div id="nav-intro" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('intro')">intro</div>
<div id="nav-terms" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('terms')">terms & definitions</div>
<div id="nav-building" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('building')">1. building the grid</div>
<div id="nav-tetrominos" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('tetrominos')">2. tetrominos</div>
<div id="nav-game" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('game')">3. game loop</div>
<div id="nav-controls" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('controls')">4. controls</div>
<div id="nav-mobile" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('mobile')">5. mobile considerations</div>
<div id="nav-final-result" class="bg-green menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('results')">7. final result</div>
<div id="nav-future" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('future')">6. future
work</div>
<div id="nav-credits" class="menu-section p-2 w-64 rounded cursor-pointer"
onclick="switchContent('credits')">credits</div>
</div>
</div>
</div>
</div>
</div>
<!-- Content Viewer List (scroll container) -->
<div class="content-viewer-outer md:ml-[400px]">
<div id="contentViewerList" class="content-viewer-list pr-12 mr-12 grid-container">
<div class="content-viewer w-full rounded-t-lg flex flex-row p-0">
<div id="intro" class="content rounded-t-lg overflow-hidden flex flex-col">
<div
class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/DALL·E 2023-12-26 15.57.30 - A visually compelling blog header image, designed in dark mode, that encapsulates the themes of artificial intelligence, programming, and Tetris, with.png"
alt="Background Image" class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Introduction</h2>
</div>
</div>
<div class="p-4">
<div class="relative block">
<p class="mb-4">Welcome to this tutorial on building Tetris with p5.js and chatGPT. In
this
tutorial, we will guide you step by step on how to create your own version of the
classic game Tetris using the p5.js library and chatGPT for behavior driven
development.
</p>
<p class="mb-4">Before we start, make sure you have a basic understanding of JavaScript
and
HTML. Knowledge in p5.js and chatGPT is not required as we will cover them in this
tutorial.</p>
<p class="mb-4">Let's get started!</p>
</div>
</div>
<div class="flex flex-row justify-between">
<button onclick="switchContent('results');"
class="bg-green-500 hover:bg-green-700 text-white font-bold p-4 m-4 rounded w-1/2">Play</button>
<button
onclick="let nextElement = this.parentElement.parentElement.parentElement.nextElementSibling; nextElement.scrollIntoView({behavior: 'smooth'}); setTimeout(() => { window.scrollBy(0, -100); }, 500);"
class="w-1/2 bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
</div>
<div class="content-viewer rounded-t-lg flex flex-row p-0" data-section-key="terms">
<div id="terms" class="content rounded-t-lg overflow-hidden flex-col">
<div
class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/9800c48a-6a59-4caa-adbc-864eec10b6de.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Terms and Definitions</h2>
</div>
</div>
<div class="p-4 flex">
<div class="parent flex flex-wrap">
<div class="child w-full md:w-1/2 p-4">
<h3 class="text-xl font-bold mb-2">Test-Driven Development (TDD)</h3>
<p class="mb-4">TDD is a software development process where you write a test before
you
write your code, and then write the code to pass the test.</p>
<h3 class="text-xl font-bold mb-2">Behavior-Driven Development (BDD)</h3>
<p class="mb-4">BDD is a software development process that encourages collaboration
between
developers, QA and non-technical or business participants in a software project.
</p>
<h3 class="text-xl font-bold mb-2">Gherkin</h3>
<p class="mb-4">Gherkin is a language designed to be easy to learn by
non-programmers
and
yet structured enough to allow concise description of examples to illustrate
business
rules in most real-world domains.</p>
<h3 class="text-xl font-bold mb-2">Cucumber</h3>
<p class="mb-4">Cucumber is a tool that supports BDD. It runs automated acceptance
tests
written in a behavior-driven development (BDD) style.</p>
</div>
<div class="child w-full md:w-1/2 p-4">
<h3 class="text-xl font-bold mb-2">Tetris</h3>
<p class="mb-4">Tetris is a popular video game that involves stacking tetrominoes to
clear
lines.</p>
<h3 class="text-xl font-bold mb-2">ChatGPT</h3>
<p class="mb-4">ChatGPT is a language model developed by OpenAI. It's capable of
generating
human-like text based on the input it's given.</p>
<h3 class="text-xl font-bold mb-2">Agent</h3>
<p class="mb-4">In the context of AI, an agent is anything that can perceive its
environment
through sensors and acts upon that environment through effectors.</p>
<h3 class="text-xl font-bold mb-2">Deep Learning, Machine Learning, Deep Neural
Networks,
Transformers, GPT</h3>
<p class="mb-4">These are all terms related to AI. Deep Learning is a subset of
Machine
Learning that uses neural networks with many layers (hence "deep"). Transformers
are
a
type of model used in machine learning. GPT (Generative Pretrained Transformer)
is a
type of transformer model developed by OpenAI.</p>
</div>
</div>
</div>
<button
onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button
onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
<div class="content-viewer rounded-t-lg flex flex-row p-0" data-section-key="building">
<div id="building" class="content rounded-t-lg overflow-hidden flex-col"">
<div class=" header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/6a6cf0d2-7a1b-4cbd-b6ec-004aa01fb20b.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Building the Grid</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">Building the Grid</h2>
<div class="clickable bg-blue-900 text-white p-4 rounded-lg mb-4 cursor-pointer" onclick="event.preventDefault(); this.querySelectorAll('.hidden, .hidden-decision').forEach((e)=>{e.classList.remove('hidden')});this.querySelector('.hideonreveal').classList.add('hidden');">
<h3 class="font-bold text-xl">Guess What's Next!</h3>
<p>What's the first decision we have to make about our game?</p>
<strong class="hideonreveal">Click to reveal the answer</strong>
<p class="hidden">How about we decide if we want to make a 2D, 3D or 2.5D game!</p>
</div>
<div class="my-4 rounded-lg p-4 border-2 border-gray-200 hidden hidden-decision">
<h3 class="text-xl font-bold mb-2">Game Dimension Decision</h3>
<p class="mb-4">This is a critical decision point in our game development process. The dimension we choose (2D, 3D, or 2.5D) will significantly impact the complexity of our game and the player's experience. Let's make a well-informed decision!</p>
<label for="gameDimension" class="text-xl font-bold mb-2">Choose the game dimension:</label>
<input type="range" id="gameDimension" name="gameDimension" min="1" max="3" value="2" class="slider w-full h-6 rounded-full">
<div class="flex justify-between text-sm font-bold mt-2">
<span>2D</span>
<span>2.5D</span>
<span>3D</span>
</div>
</div>
<div class="bg-yellow-900 text-white p-4 rounded-lg mb-4 flex flex-row justify-between">
<div>
<h3 class="font-bold text-xl">Given</h3>
<p>What's the first <strong>Given</strong> we can think of as a user?</p>
</div>
<input type="text" class="border-t-gray-600 border-x-gray-400 border-b-gray-300 border-2 text-2xl w-full bg-black text-yellow p-4 m-4 rounded-xl" placeholder="I start a new game...">
</div>
<div class="bg-purple-900 text-white p-4 rounded-lg mb-4 flex flex-row justify-between">
<div>
<h3 class="font-bold text-xl">When</h3>
<p>What's the first <strong>When</strong> we can think of as a user?</p>
</div>
<input type="text" class="border-t-gray-600 border-x-gray-400 border-b-gray-300 border-2 text-2xl w-full bg-black text-purple p-4 m-4 rounded-xl" placeholder="I wait a few frames...">
</div>
<div class="bg-orange-900 text-white p-4 rounded-lg mb-4 flex flex-row justify-between">
<div>
<h3 class="font-bold text-xl">Then</h3>
<p>What's the first <strong>Then</strong> we can think of as a user?</p>
</div>
<input type="text" class="border-t-gray-600 border-x-gray-400 border-b-gray-300 border-2 text-2xl w-full bg-black text-orange p-4 m-4 rounded-xl" placeholder="I see the first piece falling into the top row...">
</div>
<div class="bg-red-900 text-white p-4 rounded-lg mb-4 flex flex-row justify-between w-auto">
<div>
<h3 class="font-bold text-xl">Hold Up!</h3>
<p>Wait a minute, we can't assert we see a <strong>Piece</strong> on the <strong>Grid</strong> unless we assert we have a fresh <strong>Grid</strong> instance. so, let's create THAT scenario first:</p>
<div class="m-4 border-2 border-black rounded-2xl p-4 flex flex-col">
<p class="flex flex-row text-gray-400"><strong class="basis-32 text-right pr-4">Comment</strong> make sure the grid renders empty for a new game</p>
<p class="flex flex-row"><strong class="basis-32 text-right pr-4">Scenario</strong> User starts new game, sees grid</p>
<p class="flex flex-row"><strong class="basis-32 text-right pr-4">Given</strong> a fresh New Game</p>
<p class="flex flex-row"><strong class="basis-32 text-right pr-4">When</strong> I view the first frame</p>
<p class="flex flex-row"><strong class="basis-32 text-right pr-4">Then</strong> I see The Grid</p>
<div class="flex flex-row">
<button class="px-2 py-2 hover:bg-blue-600 m-4 rounded-2xl border-2 border-blue-900 bg-blue-800">Validate Scenario</button>
<button class="px-2 py-2 hover:bg-green-600 m-4 rounded-2xl border-2 border-green-900 bg-green-800">Add Scenario</button>
</div>
</div>
</div>
<!-- <input type="text" class="text-2xl w-full bg-black text-red p-4 m-4 rounded-xl" placeholder="I start a new game..."> -->
</div>
<p class="mb-4">In this section, we will discuss how to build the grid for our Tetris game using
p5.js.
The grid is a crucial component of the game as it is where the Tetris blocks will be placed
and
moved around.</p>
<p class="mb-4">We will be using a 2D array to represent our grid. Each cell in the array will
represent
a cell in the Tetris grid. We will initialize all cells to be empty at the start of the
game.
</p>
<p class="mb-4">We will also need to create a function to draw the grid on the screen. This
function
will loop through each cell in our 2D array and draw a square at the corresponding location
on
the
screen.</p>
<p class="mb-4">Finally, we will need to create a function to update the grid. This function
will be
called whenever a Tetris block is moved or rotated. It will update the 2D array to reflect
the
new
state of the grid.</p>
<form id="gridForm" class="flex justify-left items-left mb-16">
<div class="mr-4">
<label for="rows" class="text-white">Rows:</label><br>
<input type="number" id="rows" name="rows" value="20" onchange="updateGrid()"
class="bg-black text-white text-2xl w-16">
</div>
<div>
<label for="columns" class="text-white">Columns:</label><br>
<input type="number" id="columns" name="columns" value="10" onchange="updateGrid()"
class="bg-black text-white text-2xl w-16">
</div>
<div class="flex items-end mt-4 ml-4">
<button onclick="resetGrid()"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Reset</button>
</div>
</form>
<h3 class="text-xl font-bold mb-2">Prompt Output:</h3>
<p class="text-gray-500 text-sm mb-2">Click on the prompt to copy it to clipboard</p>
<button class="copyButton" onclick="copyToClipboard('building')"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
📋 Copy Prompt
</button>
<div id="promptOutput-building" class="clickable" onclick="copyToClipboard('building')"
class="cursor-pointer p-2 rounded border border-gray-300 bg-black text-white"
style="font-family: 'Courier New', monospace; white-space: pre-wrap;">Prompt will be
displayed
here...</div>
</div>
<button
onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button
onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
<div id="tetrominos" class="content-viewer rounded-t-lg flex flex-row p-0">
<div class="content rounded-t-lg overflow-hidden flex-col"">
<div class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/9f57e694-d11b-4721-9076-10a647235738.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Tetrominos</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">Section 2. Tetrominos</h2>
<p class="mb-4">In this section, we will discuss how to create the Tetrominos for our Tetris game
using
p5.js. The Tetrominos are the shapes that will be falling from the top of the screen. There are
seven different Tetrominos in total: I, J, L, O, S, T, and Z.</p>
<p class="mb-4">We will be using a 2D array to represent each Tetromino. Each cell in the array will
represent a cell in the Tetromino. We will initialize all cells to be empty at the start of the
game.</p>
<p class="mb-4">We will also need to create a function to draw the Tetromino on the screen. This
function will loop through each cell in our 2D array and draw a square at the corresponding
location
on the screen.</p>
<p class="mb-4">Finally, we will need to create a function to update the Tetromino. This function
will
be called whenever a Tetromino is moved or rotated. It will update the 2D array to reflect the
new
state of the Tetromino.</p>
<h3 class="text-xl font-bold mb-2">Prompt Output:</h3>
<p class="text-gray-500 text-sm mb-2">Click on the prompt to copy it to clipboard</p>
<button class="copyButton" onclick="copyToClipboard('tetrominos')"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
📋 Copy Prompt
</button>
<div id="promptOutput-tetrominos" class="clickable" onclick="copyToClipboard('tetrominos')"
class="cursor-pointer p-2 rounded border border-gray-300 bg-black text-white"
style="font-family: 'Courier New', monospace; white-space: pre-wrap;">Prompt will be displayed
here...</div>
<ol>
<li>
<p>Define Tetrominos: Create 2D arrays for each of the seven Tetromino shapes (I, J, L, O,
S, T,
and Z). Each cell in these arrays represents a block in the Tetromino.</p>
<pre><code class="language-javascript">const I = [[1, 1, 1, 1]], J = [[1, 0, 0], [1, 1, 1]], L = [[0, 0, 1], [1, 1, 1]], O = [[1, 1], [1, 1]], S = [[0, 1, 1], [1, 1, 0]], T = [[0, 1, 0], [1, 1, 1]], Z = [[1, 1, 0], [0, 1, 1]];</code></pre>
</li>
<li>
<p>Drawing Tetrominos: Develop a function to draw a Tetromino on the screen. This function
should loop through each cell in the Tetromino's 2D array and draw a square at the
corresponding location.</p>
<pre><code class="language-javascript">function drawTetromino(tetromino, x, y, size) {
for (let i = 0; i < tetromino.length; i++) {
for (let j = 0; j < tetromino[i].length; j++) {
if (tetromino[i][j] === 1) {
rect(x + j * size, y + i * size, size, size);
}
}
}
}
</code></pre>
</li>
<li>
<p>Updating Tetrominos: Implement a function to update the position and rotation of a
Tetromino.
This function should modify the Tetromino's 2D array to reflect its new state after
movement
or rotation.</p>
<pre><code class="language-javascript">function updateTetromino(tetromino, direction) {
// Rotate or move the tetromino based on the direction
// This is a placeholder and needs to be implemented based on the game's logic
}
</code></pre>
</li>
</ol>
<p>Tetrominos are a great usecase for "Scenario Outlines" in Gherkin BDD. We can easily define a map of valid rotations, for example, and loop through them all, reusing the same logic to validate all 7 * 4 possibilities;</p>
<p>ChatGPT Says: Don't Forget... <span></span></p>
<p class="mb-2">Remember to always test your game thoroughly. Make sure all Tetromino rotations and movements work as expected and the game ends when it should.</p>
</div>
<button
onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">
Next
</button>
</div>
</div>
<div id="game" class="content-viewer rounded-t-lg flex flex-row p-0">
<div class="content rounded-t-lg overflow-hidden flex-col"">
<div class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px]
max-h-[400px]">
<img src="./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Game Loop</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">Game Loop</h2>
<p class="mb-4">In this section, we will discuss how to create the game loop for our Tetris game using
p5.js. The game loop is responsible for updating the state of the game and drawing it on the screen.
</p>
<p class="mb-4">We will need to create a function to update the state of the game. This function will be
called every frame and will be responsible for updating the position of the Tetromino and checking
if it has collided with any other Tetrominos.</p>
<p class="mb-4">We will also need to create a function to draw the state of the game on the screen. This
function will be called every frame and will be responsible for drawing the Tetromino and the grid.
</p>
<p class="mb-4">Finally, we will need to create a function to handle user input. This function will be
called whenever the user presses a key on the keyboard. It will be responsible for moving the
Tetromino and rotating it.</p>
</div>
<button
onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
<div class="content-viewer rounded-t-lg flex flex-row p-0">
<div id="controls" class="content rounded-t-lg overflow-hidden flex-col"">
<div class=" header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/DALL·E 2023-12-26 16.45.30 - Create a pixel art image showing a dynamic scene of game development, where workers handle keyboard and touch inputs, including giant finger and keybo.png"
alt="Background Image" class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Controls</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">Controls</h2>
<p class="mb-4">In this section, we will discuss how to create the controls for our Tetris game using
p5.js. The controls are responsible for moving the Tetromino and rotating it.</p>
<p class="mb-4">We will need to create a function to handle user input. This function will be called
whenever the user presses a key on the keyboard. It will be responsible for moving the Tetromino and
rotating it.</p>
<p class="mb-4">We will also need to create a function to check if the Tetromino has collided with any
other Tetrominos. This function will be called whenever the Tetromino is moved or rotated. It will
be responsible for checking if the Tetromino has collided with any other Tetrominos.</p>
<p class="mb-4">Finally, we will need to create a function to update the state of the game. This
function will be called every frame and will be responsible for updating the position of the
Tetromino and checking if it has collided with any other Tetrominos.</p>
</div>
<button onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
<div class="content-viewer rounded-t-lg flex flex-row p-0">
<div id="mobile" class="content rounded-t-lg overflow-hidden flex-col"">
<div class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Mobile Considerations</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">Mobile Considerations</h2>
<p class="mb-4">In this section, we will discuss how to make our Tetris game touch responsive using
p5.js. The game will be playable on both desktop and mobile devices.</p>
<p class="mb-4">We will need to create a function to handle user input. This function will be called
whenever the user presses a key on the keyboard or touches the screen. It will be responsible for
moving the Tetromino and rotating it.</p>
<p class="mb-4">We will also need to create a function to check if the Tetromino has collided with any
other Tetrominos. This function will be called whenever the Tetromino is moved or rotated. It will
be responsible for checking if the Tetromino has collided with any other Tetrominos.</p>
<p class="mb-4">Finally, we will need to create a function to update the state of the game. This
function will be called every frame and will be responsible for updating the position of the
Tetromino and checking if it has collided with any other Tetrominos.</p>
</div>
<button onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
<div class="content-viewer rounded-t-lg flex flex-row p-0">
<div id="results" class="content rounded-t-lg overflow-hidden flex-col"">
<div class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">View Final Results</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">View Final Results</h2>
<p class="mb-4">In this section, you can view the final results of your Tetris game. You can see how the
game looks and behaves after implementing all the features discussed in the tutorial.</p>
<p class="mb-4">You can interact with the game, play it, and test its features. This will give you a better
understanding of how the different parts of the game work together.</p>
<p class="mb-4">Enjoy playing your Tetris game!</p>
</div>
<button onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<button onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button>
</div>
</div>
<div class="content-viewer rounded-t-lg flex flex-row p-0">
<div id="future" class="content rounded-t-lg overflow-hidden flex-col"">
<div class="header-image-wrapper relative aspect-w-16 aspect-h-9 p-0 min-h-[100px] max-h-[400px]">
<img src="./res/3d6e5e06-df51-4ef2-8b0e-1985ff458716.webp" alt="Background Image"
class="absolute inset-0 w-full h-full object-cover ">
<div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<h2 class="text-2xl font-bold mb-4">Future Work</h2>
</div>
</div>
<div class="p-4">
<h2 class="text-2xl font-bold mb-4">Future Work</h2>
<p class="mb-4">In this section, we will discuss some ideas for future work on our Tetris game using
p5.js. The game is currently playable on both desktop and mobile devices.</p>
<p class="mb-4">We could add more Tetrominos to the game. There are seven different Tetrominos in total:
I, J, L, O, S, T, and Z.</p>
<p class="mb-4">We could also add more levels to the game. Currently, there is only one level.</p>
<p class="mb-4">We could also add a high score system to the game. Currently, there is no way to keep
track of the player's score.</p>
<p class="mb-4">We could also consider adding a multiplayer mode to the game. This would allow players
to compete against each other in real-time, adding a new level of challenge and excitement to the
game.</p>
<p class="mb-4">We could also consider adding custom themes to the game. This would allow players to
personalize their gaming experience and make the game more visually appealing.</p>
<h2 class="text-2xl font-bold mb-4">Rubik's Cube Version</h2>
<p class="mb-4">Coming soon...</p>
<h2 class="text-2xl font-bold mb-4">🎉 Vote for the Next Tutorial!</h2>
<p class="mb-4">We value your opinion! Please vote for the next game you'd like to see in our series:</p>
<form>
<input type="radio" id="game1" name="game" value="game1">
<label for="game1">Space Invaders with p5.js and ChatGPT</label><br>
<input type="radio" id="game2" name="game" value="game2">
<label for="game2">Pacman with p5.js and ChatGPT</label><br>
<input type="radio" id="game3" name="game" value="game3">
<label for="game3">Snake with p5.js and ChatGPT</label><br>
<input type="submit" value="Submit">
</form>
</div>
<button onclick="this.parentElement.parentElement.previousElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-left">Prev</button>
<!-- <button
onclick="this.parentElement.parentElement.nextElementSibling.scrollIntoView({behavior: 'smooth'});"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-4 m-4 rounded float-right">Next</button> -->
</div>
</div> <!-- end div#future.content -->
<div id="credits" class="content-viewer col-span-4 text-center w-full credits bg-midnightblue text-palegoldenrod p-4 border-gray-300 border-radius rounded-lg m-4">
<p class=" text-center w-full block relative">Find this content useful? <a href="https://www.ko-fi.com/jakedowns" target="_newtab" class="text-center w-full block relative">
<img src="https://ko-fi.com/img/githubbutton_sm.svg" class="inline-block" alt="Buy Me a Coffee at ko-fi.com" /></a></p>
<p class="mt-4">
by <a href="https://patreon.com/jakedowns" target="_newtab">jake downs</a> and chatgpt. <a
href="https://github.com/jakedowns/starpages" target="_newtab">Fork this project on Github!</a> | <a
href="https://github.com/jakedowns/starpages/blob/main/tetris-with-p5js-and-chatgpt-tutorial.html"
target="_newtab">View Source Code</a>