Calendar 游戏 JavaScript 移植全解析:从 1978 年 BASIC 到浏览器端日历生成
示例工程【免费下载链接】basic-computer-gamesAn updated version of the classic Basic Computer Games book, with well-written examples in a variety of common MEMORY SAFE, SCRIPTING programming languages. See https://coding-horror.github.io/basic-computer-games/项目地址https://gitcode.com/gh_mirrors/ba/basic-computer-games点击查看免费下载本篇文章以21_Calendar目录下的 JavaScript 移植21_Calendar/javascript/README.md、calendar.js 与 calendar.html为研究对象结合原始 calendar.bas 与其他语言移植版本完整拆解这款经典程序的玩法规则、移植思路与核心算法。读完本文你将掌握这个零日期库依赖的纯算法日历生成器的工作原理并能在浏览器中直接运行它。一、游戏背景与玩法规则1.1 程序缘起Calendar 是 1978 年《Basic Computer Games》一书收录的经典程序之一由美国罗德岛州 Portsmouth 的 Abbey 修道院的 Geoffrey Chase 编写。程序的功能很纯粹打印任意年份的 12 个月日历。在今天看来这平淡无奇但在 1978 年的微型计算机时代这是一个展示字符排版与日期推算能力的经典范例。在 21_Calendar/README.md 中项目对该程序的玩法说明如下程序为任意年份打印日历你必须指定该年份一周从星期几开始闰年时需要对代码做两处修改程序的输出是一份排版工整的 12 个月月历。1.2 起始星期参数约定原程序以偏移量而非自然数来编码一年第一天的星期参数含义0星期日Sunday-1星期一Monday-2星期二Tuesday-3星期三Wednesday-4星期四Thursday-5星期五Friday-6星期六Saturday这套0 表示周日、负数向前回溯的约定来源于 BASIC 原版的注释REM 1979 STARTS ON MONDAY (0SUN, -1MON, -2TUES...)见 calendar.bas。正如 21_Calendar/README.md 所指出的玩家可以先用同仓库的WEEKDAY程序95_Weekday查出任一年份第一天的星期再据此设定参数。1.3 闰年的两处修改原版 BASIC 通过代码中两处REM注释明确指出了闰年的调整点见 calendar.bas月份天数表DATA 0,31,28,31,30,31,30,31,31,30,31,30,31中的 2 月需从 28 改为 29注释为0,31,29, ..., ON LEAP YEARS年末剩余天数标题行打印的365-S需改为366-S注释为366-S; ON LEAP YEARS。JavaScript 移植版同样保留了这两处注释便于需要时修改见 calendar.js 与第 60 行的// 366 - s on leap years。1.4 移植说明Porting Notes21_Calendar/README.md 给出了两条重要的移植约定现代环境普遍自带日期/时间函数本可以大大简化程序但这些移植版本刻意不借助日期库忠实保留原程序的纯算法风格部分移植版本选择主动询问用户起始星期与是否为闰年而不是要求玩家修改代码以提高交互性。二、JavaScript 移植整体架构JavaScript 移植由两个文件组成位于 21_Calendar/javascriptcalendar.html页面外壳负责加载脚本并渲染输出calendar.js核心算法共 89 行由 Oscar Toledo G.nanochess从 BASIC 转换而来。HTML 页面的结构非常精简见 calendar.htmlhtml head titleCALENDAR/title link relstylesheet href../../00_Utilities/javascript/style_terminal.css / /head body pre idoutput/pre script srccalendar.js/script /body /html关键点输出目标是一个pre idoutput元素保证等宽字体与空格的忠实呈现复用了仓库公共终端样式 00_Utilities/javascript/style_terminal.css让输出具有复古终端观感由于算法采用空格排版pre是唯一合适的容器——普通 HTML 会折叠连续空格破坏日历列对齐。2.1 输出原语print 与 tabJavaScript 移植没有使用console.log而是将 BASIC 的PRINT/TAB直接翻译为两个 DOM 操作函数见 calendar.jsfunction print(str) { document.getElementById(output).appendChild(document.createTextNode(str)); } function tab(space) { var str ; while (space-- 0) str ; return str; }其中print()对应 BASIC 的PRINT每次追加一段文本节点tab(n)对应TAB(n)返回 n 个空格字符串。这种每次 append 一个文本节点的方式与 BASIC 的流式打印一一对应是最直观的翻译策略。程序开头的标题打印即由二者组合而成calendar.jsprint(tab(32) CALENDAR\n); print(tab(15) CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n); print(\n); print(\n); print(\n);这与 BASIC 第 10~30 行PRINT TAB(32);CALENDAR等完全一致保留了原版的CREATIVE COMPUTING, MORRISTOWN, NEW JERSEY题头与垂直留白。三、核心算法逐段拆解整个程序不依赖任何日期 API仅靠三样东西一张月份天数表、一个累计天数变量s、以及一个全局递增的日期游标d。3.1 月份天数表与年份起点// 0, 31, 29 ON LEAP YEARS var m [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // VALUES FOR 1979 - SEE NOTES for (i 1; i 6; i) print(\n); d -1; // 1979 starts on Monday (0 Sun, -1 Monday, -2 Tuesday) s 0;数组m[0]是一个哑元占位对应 BASIC 的DIM M(12)与DATA首项 0m[1]到m[12]才是 1 月至 12 月的天数默认配置是1979 年d -1表示该年从周一开始对应 calendar.jss是本月之前已过去的天数累计初始为 0开头的 6 个空行对应 BASIC 第 120 行FOR I1 TO 6: PRINT CHR$(10): NEXT I用于在滚动终端上留出顶部空白。3.2 逐月循环与装饰性标题主体是一个for (n 1; n 12; n)的大循环calendar.js每月打印一份日历。首先是标题行s s m[n - 1]; str ** s; while (str.length 7) str ; for (i 1; i 18; i) str *; switch (n) { case 1: str JANUARY ; break; ... case 12: str DECEMBER; break; } for (i 1; i 18; i) str *; str (365 - s) **; // 366 - s on leap years print(str \n);逐段理解s s m[n-1]进入第 n 月前把上一月的天数累加进s。于是打印 1 月时s 0打印 2 月时s 31以此类推左端装饰**加累计天数如**0不足 7 列用空格补齐再接 18 个*月份名通过switch在标题中央输出月份英文名JANUARY、FEBRUARY…DECEMBER对应 BASIC 的ON N GOTO ...分支结构calendar.bas右端装饰再补 18 个*随后打印365 - s年内剩余天数与**结尾。这正是闰年需要修改的第二处把365换成366。3.3 星期表头与分隔线print( S M T W T F S\n); print(\n); str ; for (i 1; i 59; i) str *;星期表头S M T W T F S中每列占据 8 个字符宽度4 个空格 8 列单位正好对应后面日期数字的列宽设计随后打印 59 个*作为每月标题区与日期区的分隔线对应 BASIC 第 380~410 行。3.4 周网格生成日期游标算法这是全程序最巧妙的部分calendar.jsfor (week 1; week 6; week) { print(str \n); str ; for (g 1; g 7; g) { d; d2 d - s; if (d2 m[n]) { week 6; break; } if (d2 0) str d2; while (str.length 4 8 * g) str ; } if (d2 m[n]) { d g; break; } } d - g; print(str \n);算法逻辑拆解全局日期游标d程序运行期间d从-1一路单调递增每打印一个日期格子d。它表示从 1 月 1 日设为日序 0以来的天数d2 d - s用全局游标减去本月初之前的累计天数s得到本月的第几天。当d2为 0 或负数时说明该格子仍属上一个月打印空格d2 0才打印日期数字列宽控制while (str.length 4 8 * g) str ;保证每个日期占据 8 字符宽的列起始 4 空格从而与星期表头对齐边界条件if (d2 m[n])说明本月已打印完强制退出week 6 breakif (d2 m[n])恰好打完最后一天时通过d g让游标跨过本行已消费的格子数再退出行尾回退每次内层循环结束后d - g把游标回退到刚打印完的那一周的起点保证下一周从正确位置继续——这是移植中最容易出错的细节之一也是对齐打印的关键。外层最多循环 6 周week 6这是一个月最多占用的行数含空白填充保证任何月份的日历都能完整打印且各月版式一致。3.5 与 BASIC 原版的逐行对照功能BASIC 原版JavaScript 移植标题打印calendar.bas10~30 行calendar.js月份天数表calendar.basDATA 语句calendar.js数组字面量起始星期calendar.basD-1calendar.jsd -1月份名分支calendar.basON N GOTOcalendar.jsswitch星期表头calendar.bas380~400 行calendar.js周网格循环calendar.bas430~560 行calendar.js可以发现JavaScript 版几乎是一比一的结构化翻译DATA→ 数组、ON N GOTO→switch、PRINT TAB()→print(tab())甚至注释都一一保留。这为理解BASIC 结构化重构提供了绝佳范例。四、运行方式4.1 浏览器运行JavaScript 版由于 calendar.html 是纯静态页面只需在浏览器中直接打开即可# 在仓库根目录下直接用浏览器打开 open 21_Calendar/javascript/calendar.html # macOS xdg-open 21_Calendar/javascript/calendar.html # Linux页面加载后会自动执行 calendar.js在pre区域逐行渲染出 1979 年 12 个月的完整日历。页面依赖的终端样式位于 00_Utilities/javascript/style_terminal.css需要保持仓库目录结构完整才能正确显示直接复制单个 HTML 文件会丢失样式。4.2 体验其他语言移植仓库为 Calendar 提供了大量语言的移植版本各子目录的 README 均声明原始源码下载自 Vintage Basic转换为对应语言例如21_Calendar/python/calendar.pyPython21_Calendar/csharp/Program.csC#21_Calendar/java/Calendar.javaJava21_Calendar/ruby/calendar.rbRuby21_Calendar/rust/src/main.rsRust21_Calendar/perl/calendar.plPerl其中 C# 有完整的 Calendar.csproj 与 Calendar.sln 工程文件Rust 版则带 Cargo.toml 与 Cargo.lock均可按对应语言的常规方式直接运行如python 21_Calendar/python/calendar.py、cargo run。各个移植版在算法上与 JavaScript 版同源对照阅读可以横向比较不同语言对同一算法的表达方式。五、运行效果与验证要点程序运行后应输出 12 个月份的月历每个月的版式一致可重点核对以下两点列对齐星期表头S M T W T F S与每个日期数字严格按 8 字符列对齐1 月 1 日应落在 Monday 一列之下默认d -1月份衔接相邻两个月的日期不重不漏。例如 1 月打印到 31 日2 月的第一天应紧接其后且 2 月有 28 天非闰年默认配置。验证时还可修改 calendar.js 的d值与第 25 行的二月天数观察不同起始星期与闰年配置下的输出变化——这正是原版要求玩家自行修改的设计意图。六、总结Calendar 的 JavaScript 移植是一份小而美的经典移植样例不足百行代码却完整承载了 1978 年 BASIC 原版的全部算法逻辑——累计天数、日期游标、8 字符列宽排版、闰年双修改点。它刻意绕开现代日期库用最朴素的纯算法实现日历生成既保留了原版的可玩性自行修改参数又为学习者提供了一份低门槛、可对照 BASIC 逐行研读的移植教材。若想进一步理解程序在真实环境中的行为直接运行 calendar.html 即可在浏览器中复现这份来自 1978 年的复古月历。赞分享示例工程【免费下载链接】basic-computer-gamesAn updated version of the classic Basic Computer Games book, with well-written examples in a variety of common MEMORY SAFE, SCRIPTING programming languages. See https://coding-horror.github.io/basic-computer-games/项目地址https://gitcode.com/gh_mirrors/ba/basic-computer-games点击查看免费下载相关推荐MiniScript 移植版 Calendar 程序解析从 1978 年 BASIC 经典到可交互的年历打印器MiniScript 移植版 Calendar 程序解析从 1978 年 BASIC 经典到可交互的年历打印器 导读 本文围绕仓库中的 MiniScript示例工程Bug 游戏 Python 移植版源码解析从 1978 年 BASIC 到 dataclass 的骰子拼虫游戏Bug 游戏 Python 移植版源码解析从 1978 年 BASIC 到 dataclass 的骰子拼虫游戏 本指南以 16_Bug/python/READ示例工程Archon Monorepo 上下文预热指南用 Prime 命令为 AI Agent 建立完整的代码库认知Archon Monorepo 上下文预热指南用 Prime 命令为 AI Agent 建立完整的代码库认知 本文围绕 Archon 仓库中面向编码 Agen示例工程上一篇Android序列化利器Parceler库详解下一篇OpenBidKit_Yibiao文件解析原理如何让AI精准理解招标文件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

bzip2-ffi 源码深度剖析:@C 结构体与 CString 内存管理实现原理完整指南

bzip2-ffi 源码深度剖析:@C 结构体与 CString 内存管理实现原理完整指南

bzip2-ffi 源码深度剖析:C 结构体与 CString 内存管理实现原理完整指南 【免费下载链接】bzip2-ffi 一个用于创建和解压bzip2压缩格式的库 项目地址: https://gitcode.com/Cangjie-TPC/bzip2-ffi bzip2-ffi 是一款基于仓颉语言的 bzip2 压缩库,通…

2026/9/24 17:17:24 阅读更多 →
冲锋衣内衬北极绒怎么选?154g/㎡单面绒参数解析与采购要点

冲锋衣内衬北极绒怎么选?154g/㎡单面绒参数解析与采购要点

冲锋衣内衬选用北极绒,不能只比较柔软度、克重和单价。更有效的选料方法是:先明确使用部位和成衣结构,再核对面料规格,通过样布与样衣验证,最后确定大货验收和供应条件。本文以154g/㎡经编单面北极绒为例,梳…

2026/9/24 17:17:24 阅读更多 →
基于Keras构建模型并部署到STM32F4的完整指南

基于Keras构建模型并部署到STM32F4的完整指南

基于 Keras 构建模型并部署到 STM32F4 的完整指南 概述 将基于 Keras 构建的模型部署到 STM32F4 微控制器上,属于典型的 TinyML(微型机器学习)应用。由于 STM32F4 的硬件资源(如 Flash 和 RAM)相对有限,无法…

2026/9/24 17:17:24 阅读更多 →

最新新闻

卡车倾倒建筑垃圾检测数据集:从视频流到行为识别的落地拆解

卡车倾倒建筑垃圾检测数据集:从视频流到行为识别的落地拆解

简介:这是一份面向计算机视觉与深度学习方向的目标检测数据集,聚焦卡车倾倒建筑垃圾这一特定行为识别任务,适合训练和评估YOLOv7等实时检测模型,可服务于城市监控、建筑工地管理与环保监测等场景。压缩包共1023个文件,…

2026/9/24 19:32:03 阅读更多 →
心脏病预测机器学习实战:11个脚本从数据清洗到XGBoost调参

心脏病预测机器学习实战:11个脚本从数据清洗到XGBoost调参

简介:这份资源面向机器学习入门与进阶学习者,提供一套完整的心脏病数据集分析与预测实战案例,帮助读者掌握从数据清洗、特征工程到多模型对比的完整流程。包内共14个文件,以11个Python源代码为主,另含2个CSV数据集和1个…

2026/9/24 19:32:03 阅读更多 →
基于销量可视化的手机价位段智能选型平台

基于销量可视化的手机价位段智能选型平台

开头做手机选品或者门店铺货的朋友,应该都有过这种纠结:同一批预算,到底是多进几台千元机走量,还是押两三部旗舰机赚毛利?以前大家基本靠经验和感觉,但感觉这东西在行情波动面前特别不靠谱。我去年接手了一…

2026/9/24 19:32:03 阅读更多 →
东华OJ刷题复盘:从TLE到AC,避开多组输入与边界陷阱

东华OJ刷题复盘:从TLE到AC,避开多组输入与边界陷阱

连着刷了三个晚上,东华OJ的基础练习终于推进到了第7到第9题。说实话,这三道题单独拎出来都不算难,但它们卡我的时间和心态,比后面那些看起来更复杂的题还要狠。第7题让我第一次在OJ上感受到“Time Limit Exceeded”的分量&#xf…

2026/9/24 19:32:03 阅读更多 →
SAP选择性数据迁移实施商选型:2026年避坑指南

SAP选择性数据迁移实施商选型:2026年避坑指南

2026年,很多SAP老客户心里都装着一件事:ECC到底什么时候迁,怎么迁。而在这个大问题下面,真正让人头疼的其实是另一个更具体的问题——选择性数据迁移,到底该选哪家SAP实施商来干。先别急着谈价格、谈人天,我…

2026/9/24 19:32:03 阅读更多 →
MySQL用户管理与权限设置实战:从GRANT到远程连接排查

MySQL用户管理与权限设置实战:从GRANT到远程连接排查

接手过不少MySQL环境,也帮人排查过很多数据库问题,发现真正让运维和开发头疼的,往往不是SQL写得不好,而是用户管理和权限设置这块没搞清爽。尤其是线上环境,账号多了、权限乱了,要么是开发抱怨连不上库&…

2026/9/24 19:31:02 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →