10浏览
查看: 10|回复: 3

[项目] 【花雕动手做】基于Kitronik可编程开发板之康威生命游戏

[复制链接]
Kitronik ARCADE 是一款由英国教育科技公司 Kitronik 精心打造的可编程游戏机开发板,专为编程教学与创客实践而设计。该设备原生支持微软的 MakeCode Arcade 平台,用户可通过图形化或 JavaScript 编程方式,轻松创建、下载并运行复古风格的街机游戏。

它集成了彩色 LCD 显示屏、方向控制键、功能按键、蜂鸣器和震动马达等交互组件,提供完整的游戏输入输出体验。无论是初学者进行编程启蒙,还是创客群体开发交互式作品,Kitronik ARCADE 都能作为理想的硬件载体,助力创意实现。

凭借其开源友好、易于上手、兼容性强等特点,该开发板广泛应用于中小学编程课程、创客工作坊、游戏开发教学以及个人项目原型设计,深受教育者与技术爱好者的喜爱。

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏图2

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏图1

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏图3

驴友花雕  中级技神
 楼主|

发表于 2 小时前

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏

作为学习、练习与尝试,这里创建一个康威生命的小游戏。
打开网页版:https://arcade.makecode.com/,设置项目名称:康威生命

JavaScript 实验代码

  1. enum StillLife {
  2.     Block,
  3.     Beehive,
  4.     Loaf,
  5.     Boat,
  6.     Tub
  7. }
  8. enum Oscillator {
  9.     Blinker,
  10.     Toad,
  11.     Beacon,
  12.     Pulsar,
  13.     Pentadecathlon
  14. }
  15. enum Motion {
  16.     Glider,
  17.     LightWeight,
  18.     Gospers,
  19.     Simkins,
  20.     SimkinsDouble,
  21.     Engine,
  22.     BlockLayer
  23. }
  24. enum OddCell {
  25.     RPentomino,
  26.     DieHard,
  27.     Acorn
  28. }
  29. const width = screen.width;
  30. const height = screen.height;
  31. scene.setBackgroundImage(image.create(width, height));
  32. // test1();
  33. test2();
  34. // test3();
  35. function test1() {
  36.     cells.createRandom(4000);
  37. }
  38. function test2() {
  39.     for (let i = 0; i < 15; ++i) {
  40.         for (let j = 0; j < 6; ++j) {
  41.             cells.createOscillator(Oscillator.Pentadecathlon, 8 + 10 * i, 5 + 20 * j);
  42.         }
  43.     }
  44. }
  45. function test3() {
  46.     for (let i = 0; i < 5; ++i) {
  47.         cells.createMotion(Motion.Gospers, 15, 2 + i * 25);
  48.     }
  49. }
  50. // buffers[bufferNum][x][y] corresponds to whether the cell at location (x,y)
  51. // was alive in the given buffer
  52. let buffers: boolean[][][];
  53. let currentBuffer: number;
  54. init();
  55. game.onUpdateInterval(100, nextGeneration);
  56. // game.onUpdate(nextGeneration);
  57. function countNeighbors(src: boolean[][], x: number, y: number): number {
  58.     const lX = x - 1; // left x
  59.     const rX = x + 1; // right x
  60.     const lY = y - 1; // left y
  61.     const rY = y + 1; // right y
  62.     let count = 0;
  63.     if (src[lX][lY])++count;
  64.     if (src[lX][y])++count;
  65.     if (src[lX][rY])++count;
  66.     if (src[x][lY])++count;
  67.     if (src[x][rY])++count;
  68.     if (src[rX][lY])++count;
  69.     if (src[rX][y])++count;
  70.     if (src[rX][rY])++count;
  71.     return count;
  72. }
  73. function nextGeneration() {
  74.     const lastGeneration = buffers[currentBuffer % 2];
  75.     const currGeneration = buffers[++currentBuffer % 2];
  76.     const bkgd = scene.backgroundImage();
  77.     // leave 1 pixel of unused edge on each side
  78.     // to avoid having to deal with oob checking
  79.     for (let x = 1; x < width - 1; ++x) {
  80.         for (let y = 1; y < height - 1; ++y) {
  81.             const neighbors = countNeighbors(lastGeneration, x, y);
  82.             if (lastGeneration[x][y] && (neighbors < 2 || neighbors > 3)) {
  83.                 // Previously alive cell has died due to under- or over-population
  84.                 currGeneration[x][y] = false;
  85.                 bkgd.setPixel(x, y, 0);
  86.             } else if (!lastGeneration[x][y] && neighbors == 3) {
  87.                 // Previously empty location has new cell born
  88.                 currGeneration[x][y] = true;
  89.                 bkgd.setPixel(x, y, randint(1, 0xd));
  90.             } else {
  91.                 // State is unchanged
  92.                 currGeneration[x][y] = lastGeneration[x][y];
  93.             }
  94.         }
  95.     }
  96. }
  97. function init() {
  98.     const bkgd = scene.backgroundImage();
  99.     buffers = [[], []];
  100.     for (let x = 0; x < width; x++) {
  101.         buffers[0][x] = [];
  102.         buffers[1][x] = [];
  103.         for (let y = 0; y < height; y++) {
  104.             buffers[0][x][y] = bkgd.getPixel(x, y) != 0;
  105.             buffers[1][x][y] = false;
  106.         }
  107.     }
  108.     currentBuffer = 0;
  109.     // Draw a border around screen, as those pixels are counted as 'not alive'
  110.     for (let x = 0; x < width; x++) {
  111.         buffers[0][x][0] = false;
  112.         buffers[0][x][height - 1] = false;
  113.         bkgd.setPixel(x, 0, 1);
  114.         bkgd.setPixel(x, height - 1, 1);
  115.     }
  116.     for (let y = 0; y < height; y++) {
  117.         buffers[0][0][y] = false;
  118.         buffers[0][width - 1][y] = false;
  119.         bkgd.setPixel(0, y, 1);
  120.         bkgd.setPixel(width - 1, y, 1);
  121.     }
  122. }
  123. namespace cells {
  124.     export function createStillLife(toDisplay: StillLife,
  125.         x: number,
  126.         y: number,
  127.         src?: Image) {
  128.         if (!src) src = scene.backgroundImage();
  129.         let display: Image;
  130.         switch (toDisplay) {
  131.             case StillLife.Block: {
  132.                 display = img`
  133.                     1 1
  134.                     1 1
  135.                 `
  136.                 break;
  137.             }
  138.             case StillLife.Beehive: {
  139.                 display = img`
  140.                     . 1 1 .
  141.                     1 . . 1
  142.                     . 1 1 .
  143.                 `
  144.                 break;
  145.             }
  146.             case StillLife.Loaf: {
  147.                 display = img`
  148.                     . 1 1 .
  149.                     1 . . 1
  150.                     . 1 . 1
  151.                     . . 1 .
  152.                 `
  153.                 break;
  154.             }
  155.             case StillLife.Boat: {
  156.                 display = img`
  157.                     1 1 .
  158.                     1 . 1
  159.                     . 1 .
  160.                 `
  161.                 break;
  162.             }
  163.             case StillLife.Block: {
  164.                 display = img`
  165.                     1 1
  166.                     1 1
  167.                 `
  168.                 break;
  169.             }
  170.             case StillLife.Tub: {
  171.                 display = img`
  172.                     . 1 .
  173.                     1 . 1
  174.                     . 1 .
  175.                 `
  176.                 break;
  177.             }
  178.             default: return;
  179.         }
  180.         src.drawImage(display, x, y);
  181.     }
  182.     export function createOscillator(toDisplay: Oscillator,
  183.         x: number,
  184.         y: number,
  185.         src?: Image) {
  186.         if (!src) src = scene.backgroundImage();
  187.         let display: Image;
  188.         switch (toDisplay) {
  189.             case Oscillator.Blinker: {
  190.                 display = img`
  191.                     1
  192.                     1
  193.                     1
  194.                 `
  195.                 break;
  196.             }
  197.             case Oscillator.Toad: {
  198.                 display = img`
  199.                     . 1 1 1
  200.                     1 1 1 .
  201.                 `
  202.                 break;
  203.             }
  204.             case Oscillator.Beacon: {
  205.                 display = img`
  206.                     1 1 . .
  207.                     1 1 . .
  208.                     . . 1 1
  209.                     . . 1 1
  210.                 `
  211.                 break;
  212.             }
  213.             case Oscillator.Pulsar: {
  214.                 display = img`
  215.                     . . . . 1 . . . . . 1 . . . .
  216.                     . . . . 1 . . . . . 1 . . . .
  217.                     . . . . 1 1 . . . 1 1 . . . .
  218.                     . . . . . . . . . . . . . . .
  219.                     1 1 1 . . 1 1 . 1 1 . . 1 1 1
  220.                     . . 1 . 1 . 1 . 1 . 1 . 1 . .
  221.                     . . . . 1 1 . . . 1 1 . . . .
  222.                     . . . . . . . . . . . . . . .
  223.                     . . . . 1 1 . . . 1 1 . . . .
  224.                     . . 1 . 1 . 1 . 1 . 1 . 1 . .
  225.                     1 1 1 . . 1 1 . 1 1 . . 1 1 1
  226.                     . . . . . . . . . . . . . . .
  227.                     . . . . 1 1 . . . 1 1 . . . .
  228.                     . . . . 1 . . . . . 1 . . . .
  229.                     . . . . 1 . . . . . 1 . . . .
  230.                 `
  231.                 break;
  232.             }
  233.             case Oscillator.Pentadecathlon: {
  234.                 display = img`
  235.                     1 1 1
  236.                     . 1 .
  237.                     . 1 .
  238.                     1 1 1
  239.                     . . .
  240.                     1 1 1
  241.                     1 1 1
  242.                     . . .
  243.                     1 1 1
  244.                     . 1 .
  245.                     . 1 .
  246.                     1 1 1
  247.                 `
  248.                 break;
  249.             }
  250.             default: return;
  251.         }
  252.         src.drawImage(display, x, y);
  253.     }
  254.     export function createMotion(toDisplay: Motion,
  255.         x: number,
  256.         y: number,
  257.         src?: Image) {
  258.         if (!src) src = scene.backgroundImage();
  259.         let display: Image;
  260.         switch (toDisplay) {
  261.             case Motion.Glider: {
  262.                 display = img`
  263.                     . 1 .
  264.                     . . 1
  265.                     1 1 1
  266.                 `
  267.                 break;
  268.             }
  269.             case Motion.LightWeight: {
  270.                 display = img`
  271.                     1 . . 1 .
  272.                     . . . . 1
  273.                     1 . . . 1
  274.                     . 1 1 1 1
  275.                 `
  276.                 break;
  277.             }
  278.             case Motion.Gospers: {
  279.                 display = img`
  280.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  281.                     . . . . . . . . . . . . . . . . . . . . . . . 1 1 . . . . . . . . . . .
  282.                     . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 . . . . . . . . . .
  283.                     . . . . . . . . . 1 . . . . . . . . . . . . . . . 1 1 . 1 . . . . . 1 1
  284.                     . . . . . . . 1 . 1 . . . . 1 1 1 . . . . . . . . 1 . . 1 . . . . . 1 1
  285.                     1 1 . . . . 1 . 1 . . . . . . . . . . . . . . . . 1 1 . 1 . . . . . . .
  286.                     1 1 . . . 1 . . 1 . . . . . . . 1 . . 1 1 . . 1 1 1 . . . . . . . . . .
  287.                     . . . . . . 1 . 1 . . . . . . . 1 . . . 1 . . 1 1 . . . . . . . . . . .
  288.                     . . . . . . . 1 . 1 . . . . . . 1 . . 1 . . . . . . . . . . . . . . . .
  289.                     . . . . . . . . . 1 . . . . . . . . 1 1 . . . . . . . . . . . . . . . .
  290.                 `;
  291.                 break;
  292.             }
  293.             case Motion.Simkins: {
  294.                 display = img`
  295.                     1 1 . . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . .
  296.                     1 1 . . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . .
  297.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  298.                     . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . . . . .
  299.                     . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . . . . .
  300.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  301.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  302.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  303.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  304.                     . . . . . . . . . . . . . . . . . . . . . . 1 1 . 1 1 . . . . . .
  305.                     . . . . . . . . . . . . . . . . . . . . . 1 . . . . . 1 . . . . .
  306.                     . . . . . . . . . . . . . . . . . . . . . 1 . . . . . . 1 . . 1 1
  307.                     . . . . . . . . . . . . . . . . . . . . . 1 1 1 . . . 1 . . . 1 1
  308.                     . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . . . . . .
  309.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  310.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  311.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  312.                     . . . . . . . . . . . . . . . . . . . . 1 1 . . . . . . . . . . .
  313.                     . . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . . . . .
  314.                     . . . . . . . . . . . . . . . . . . . . . 1 1 1 . . . . . . . . .
  315.                     . . . . . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . .
  316.                 `
  317.                 break;
  318.             }
  319.             case Motion.SimkinsDouble: {
  320.                 display = img`
  321.                     1 1 . . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . .
  322.                     1 1 . . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . .
  323.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  324.                     . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . . . . .
  325.                     . . . . 1 1 . . . . . . . . . . . . . . . . . . . . . . . . . . .
  326.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  327.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  328.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  329.                     . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  330.                     . . . . . . . . . . . . . . . . . . . . . . 1 1 . 1 1 . . . . . .
  331.                     . . . . . . . . . . . . . . . . . . . . . 1 . . . . . 1 . . . . .
  332.                     . . . . . . . . . . . . . . . . . . . . . 1 . . . . . . 1 . . 1 1
  333.                     . . . . . . . . . . . . . . . . . . . . . 1 1 1 . . . 1 . . . 1 1
  334.                     . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . . . . . .
  335.                 `
  336.                 break;
  337.             }
  338.             case Motion.Engine: {
  339.                 display = img`
  340.                     1 1 1 1 1 1 1 1 . 1 1 1 1 1 . . . 1 1 1 . . . . . . 1 1 1 1 1 1 1 . 1 1 1 1 1
  341.                 `
  342.                 break;
  343.             }
  344.             case Motion.BlockLayer: {
  345.                 display = img`
  346.                     1 1 1 . 1
  347.                     1 . . . .
  348.                     . . . 1 1
  349.                     . 1 1 . 1
  350.                     1 . 1 . 1
  351.                 `
  352.                 break;
  353.             }
  354.             default: return;
  355.         }
  356.         src.drawImage(display, x, y);
  357.     }
  358.     export function createOddCell(toDisplay: OddCell,
  359.         x: number,
  360.         y: number,
  361.         src?: Image) {
  362.         if (!src) src = scene.backgroundImage();
  363.         let display: Image;
  364.         switch (toDisplay) {
  365.             case OddCell.RPentomino: {
  366.                 display = img`
  367.                     . 1 1
  368.                     1 1 .
  369.                     . 1 .
  370.                 `
  371.                 break;
  372.             }
  373.             case OddCell.DieHard: {
  374.                 display = img`
  375.                     . . . . . . 1 .
  376.                     . . . . . . . .
  377.                     1 1 . . . . 1 .
  378.                     . 1 . . . 1 1 1
  379.                 `
  380.                 break;
  381.             }
  382.             case OddCell.Acorn: {
  383.                 display = img`
  384.                     . 1 . . . . .
  385.                     . . . 1 . . .
  386.                     1 1 . . 1 1 1
  387.                 `;
  388.                 break;
  389.             }
  390.             default: return;
  391.         }
  392.         src.drawImage(display, x, y);
  393.     }
  394.     export function createRandom(count: number, src?: Image) {
  395.         if (!src) src = scene.backgroundImage();
  396.         for (let i = 0; i < count; ++i)
  397.             src.setPixel(randint(0, width), randint(0, height), 1);
  398.     }
  399. }
复制代码



回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2 小时前

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏

这段代码实现了康威生命游戏(Conway's Game of Life),这是一个著名的细胞自动机模拟。
代码结构分析

1. 枚举类型定义
定义了四种模式枚举:
• StillLife: 静态稳定结构(不会变化)
• Oscillator: 振荡器(周期性变化)
• Motion: 移动结构(会在空间中移动)
• OddCell: 特殊细胞结构

2. 初始化设置
• 使用屏幕宽度和高度创建背景图像
• 提供了三种测试模式

3. 核心逻辑
• buffers: 双缓冲区系统,用于存储当前和下一代细胞状态
• countNeighbors(): 计算细胞周围存活邻居数量
• nextGeneration(): 应用生命游戏规则生成下一代
• init(): 初始化缓冲区和边界

4. 细胞模式库
cells命名空间提供了多种预定义模式:
• 静态结构(Block, Beehive, Loaf等)
• 振荡器(Blinker, Toad, Beacon等)
• 移动结构(Glider, LightWeight, Gospers等)
• 特殊结构(RPentomino, DieHard, Acorn)
• 随机生成功能

生命游戏规则实现
代码实现了标准规则:
5. 存活细胞周围少于2个存活邻居 → 死亡(孤独)
6. 存活细胞周围有2或3个存活邻居 → 继续存活
7. 存活细胞周围超过3个存活邻居 → 死亡(过度拥挤)
8. 死亡细胞周围恰好有3个存活邻居 → 新生
9.
可视化效果
• 使用背景图像的像素表示细胞状态
• 新生的细胞会随机分配颜色(1-13)
• 边界用颜色1标记

可能的改进方向
10. 添加用户交互(点击创建/删除细胞)
11. 增加速度控制
12. 添加更多预定义模式
13. 实现模式保存/加载功能
14. 添加统计信息(代数、细胞数量等)
这个实现充分利用了MakeCode Arcade的图形功能,提供了一个视觉上吸引人的生命游戏模拟。双缓冲区技术确保了平滑的世代过渡,而预定义模式库让用户可以轻松探索生命游戏的各种有趣行为。


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2 小时前

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏

通过模拟器,调试与模拟运行

【花雕动手做】基于Kitronik可编程开发板之康威生命游戏图1
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail