我接触过许多学生和专业开发者,他们经常想做个副业,但不知从何做起。下面我将分享一些能让我学到很多东西的软件项目,这些项目都非常棒,你可以多次构建它们,且每次都能学到新东西。因此,每当我不知道该构建什么、或想学习一种新的编程语言或框架时,我都会从这些项目中挑选一个开始做:
文本编辑器
2D 游戏 – 《太空侵略者》
编译器 – Tiny BASIC
迷你操作系统
电子表格(难)
电子游戏机模拟器(难)
文本编辑器
我们几乎每天都在用文本编辑器,但你知道它到底是如何工作的吗?忽略掉你最喜欢的编辑器中所有的花哨功能,你会如何实现一个支持可移动文本光标以及选择、插入和删除文本的文本框?记住,你不能使用你最喜欢的 GUI 框架中的内置文本框组件!
最大的挑战在于如何在内存中存储文本文档。我首先想到的是使用数组,但如果用户插入的文本位置不在末尾,那么数组的性能就会非常糟糕。幸运的是,我们可以学习一些不错的数据结构来解决这个问题。
另一个障碍是了解文本光标在常用编辑器中的行为。例如,如果我在文档中间按向上箭头键,光标会移动到哪里?上方的同一列吗?实际上,如果那行比较短就不会。不断按向上键,直到有一行足够长光标才会回到原始列——原来,光标对列是有记忆的,并会试图回到该列。这些细节,在我尝试实现它之前,从未注意到。
在实现了基本的文本编辑器功能后,我觉得你可以再实现两个功能:撤消/重做和自动换行。以一种高效的方式实现撤销/重做对我来说,真是令人惊叹的体验!我先是尝试保留先前状态的数组,然后又尝试了 Memento 模式,最后才选择了 Command 模式。自动换行则要求你将文本行的视觉方面与内存方面分开。
需要学习的内容:
1.存储文本的数据结构:数组、rope、间隙缓冲区、piece table。
2.文本光标的行为和实现。
3.撤销/重做的设计模式:备忘录(memento)、命令(command)。
4.分离文本的视觉和内存方面的抽象。
2D 游戏 – 《太空侵略者》
即使是最简单的游戏,也需要一些独特的数据结构和设计模式。我的想法是从头到尾实现一个定义明确的游戏,而不被其他有趣的东西(如游戏设计和美术)所困扰。此外,最好使用一个简单的 2D 图形库(如 SDL、SFML、PyGame)而不是一个大型游戏引擎,因为后者会隐藏所有有趣的部分。
第一,学会如何将图像绘制到屏幕上。我之前对此一无所知,后来才知道这实际上是以极快的速度每秒多次地清空屏幕,然后逐个绘制屏幕的各个部分,以创造物体在移动的效果。
第二,学习有关游戏循环的一切。一个游戏实际上就是在绘制、获取用户输入和处理游戏逻辑之间循环。
第三,学习如何处理用户输入。我以前从未注意到最初按下、持续按住和松开按键或鼠标按钮的细微差别,更不用说处理双击等操作了。你多久检查一次用户输入?如果你一直在检查,那意味着游戏的其他部分被冻结了!
第四,学习如何创建和管理所有游戏对象及其状态。例如,如何生成动态数量的敌人?在这个方面,工厂模式(Factory Pattern)帮了大忙。
第五,学习如何应用游戏逻辑。例如,何时更新子弹位置?何时出现更多的敌人?如何知道敌人被消灭了?游戏何时结束?在制作游戏之前,我从未使用过 modulo 运算符,但它现在遍布在我的游戏代码中。
一旦你把基本游戏做出来,就可以尝试添加一个标题屏幕菜单、游戏结束屏幕,确保游戏在不同的计算机上以相同速度运行,并探索如何利用 AI 实现更有趣的敌人。你觉得这样还不够?那就添加着色器效果、声音和在线多人游戏!
需要学习的内容:
1.在屏幕上绘图。
2.处理用户输入。
3.游戏循环。
4.创建和管理动态数量的对象(如工厂模式)。
5.用AI制作更多敌人。
6.播放声音。
7.使用着色器。
8.实现在线功能。
编译器 – Tiny BASIC
编译器是我参与过的最令人大开眼界的项目,即使是现在,我也会在周日下午抽空写些代码,有时候就会选择写一个编译器——当你创造出的东西,能让别人创造出更多的东西时,这种感觉很棒。想要实现一个编译器,我不得不了解编译器的复杂性,而这些复杂性是我平时根本不会想的(例如表达式何时会进行隐式类型转换)
我建议从头开始编写一个类似于 BASIC 的小型语言编译器,然后将其编译成你熟悉的任何其他语言。例如,你可以用 Python 编写一个 Tiny BASIC 编译器,输出 C# 代码。它不一定要输出汇编或 C,避免使用这些语言可以让你专注于编译器本身。
第一个难题是,弄清如何对输入代码进行词法分析(或标记化)。然后是解析代码,检查输入代码的结构并生成代码的树形表示,递归下降解析技术非常有意思!接下来,你将对输入内容进行语义检查,确保代码是合理的,并且遵循类型规则。最后,你就可以生成输出了!
这个项目有很多现有资源可以帮助你,一个简单的编译器在几天内就能完成,不要被专业术语吓倒。此外,你还可以添加无限的可能性!有了基本的编译器后,你就可以添加标准库(在 PeayBASIC 中,我添加了简单的 2D 图形功能)、优化步骤并改进错误消息。最后,你还可以用自己的语言编写一些示例程序,向全世界展示你的成果!
需要学习的内容:
1.词法分析。
2.句法分析。
3.递归下降解析。
4.抽象语法树。
5.语义分析。
6.优化传递。
7.代码生成。
迷你操作系统
多年来,我发现自己将操作系统的基本概念应用到各种领域,比如游戏,甚至还有人类行为预测模型。有时候在课堂上,操作系统用的算法和数据结构可能看起来很抽象或无用,但它们实际上非常有用。实现一个操作系统,还能帮助我更深入地了解系统底层的运行原理。
由于操作系统依赖于硬件,因此在入门时可能会有一些障碍,学习曲线较为陡峭。不过只要照着书或教程做,你就能得到一个可启动的操作系统,并可以运行自己的程序,在此,我强烈推荐一本免费在线书籍《使用 Rust 制作 RISC-V 操作系统》(https://osblog.stephenmarz.com/index.html)。
需要学习的内容:
1.交叉编译
2.启动加载
3.BIOS 中断
4.x86 模式
5.内存管理和分页
6.调度(如循环调度)
7.文件系统(如 FAT)
还觉得不够难?再试试电子表格和游戏机模拟器
电子表格
类似 Excel 的电子表格应用程序,结合了文本编辑器和编译器所面临的一些挑战。你必须学会如何在内存中表示单元格内容,并为用于方程式的编程语言实现解释器。
电子游戏机模拟器
编写一个电子游戏机模拟器,意味着将实现操作系统和编译器的挑战结合到一起,而用你的模拟器玩别人制作的真实游戏是一件非常有成就感的事情!
模拟真实的电子游戏机意味着编写一个虚拟机,假装像实际的 CPU 和其他硬件组件一样运行。这样,你就可以用模拟器运行为电子游戏机设计的游戏。
我建议首先模拟 CHIP-8,这是一个简单的虚构主机,然后再转向真正的电子游戏机。NES、SNES、Gameboy 和 Gameboy Advance 都是可以模拟的,也都有相当多的文档和开源模拟器,不过它们都有一些自己的“怪癖”,会让事情变得有趣(例如,某些游戏可能依赖于特定硬件的未记录错误/功能)。还有 PICO-8,它已成为一种非常赚钱的“梦幻主机”。
开发者:“基于现成构件开发的软件,未来将由 AI 编写”
对于 Austin Z. Henley 分享的这 6 个项目,不少开发者表示认同,还另外提出了其他合适的开发项目:
“迷你操作系统 +1。像我们这种应用开发者,依赖于操作系统的许多功能:内存管理、文件系统等。我相信我们最终都会问一个问题:‘这些事情是如何在幕后完成的?’这就是我在业余时间捣鼓这类项目的原因,我也学到了很多东西。虽然学到的知识可能与我工作的相关性几乎为零,但这绝对是一件有趣的事。”
“我认为一个小小的玩具光线跟踪器也是一个不错的尝试。只需输出球体的位图图形,并进行漫反射和镜面反射,再加上几个光源即可。如果你不太疯狂的话,这应该是一个相对独立的项目。”
不过,也有开发者指出做这种项目意义不大,推荐可以创建一个网络搜索引擎:
“我认为以下这些软件工程必备技能,需要被重视和实践:(1)决定自己写什么,从库中获取什么;(2)确定满足项目需求的高质量库和框架;(3)决定哪些地方值得优化,哪些地方不值得优化;(4)编写几年后你自己(和其他人)仍可阅读的代码;(5)将项目视为一个具有软件和非软件依赖关系的大型复杂系统。
本着这种精神,我认为创建一个网络搜索引擎更好。你不用费心研究字符串匹配算法等,因为别人已经为你做过了。你只需制作一个能真正运行的搜索引擎,哪怕一开始只支持网络的一个子集和一个并发用户也好。”
对于这种“基于现有构件开发项目”的想法,有开发者持反对意见,认为未来这类软件的开发会被 AI 取代:
“这听起来是个明智的建议,但是纯粹利用现成的构件来制作软件存在一些问题:(1)很多时候,现成构件都很垃圾。如果你的软件基于它们,那么你的生活将不是编写软件,而是维护和修改垃圾;(2)即使你的现成构件是高质量的,它们也会限制你编写软件。理想的情况是,你先想象你想让软件做什么,然后再编写软件来实现它。而在这个过程中,你大概率会自己创建构件,因为只使用现成构件是无法编写出很多优秀软件的。
另外我还要告诉你一个坏消息,这种软件很快就不是由你编写了,而是由 AI 编写的。”
那么对于本文作者提出的 6 个“每个程序员都应该尝试的”开发项目,作为开发者的你又有什么看法吗?
https://austinhenley.com/blog/challengingprojects.html
https://news.ycombinator.com/item?id=38768678