程序员用12小时复刻《羊了个羊》,代码已开源!

来源 | CSDN(ID:CSDNnews)
作者 | 开发游戏的老王
责编 | 张红月
 
【CSDN 编者按】过去一周,不少人被《羊了个羊》这款游戏虐的不轻,有多少个“再玩一把”的念头,就有多少次被打入深渊的凄凉,甚至还有人评价道:“什么事都可以过去,除了《羊了个羊》第二关”。因此,有用户抱怨是“程序员故意挖坑制作死关卡”。然而在本文作者老王一探究竟以后,才发现并非程序员挖坑,而是该游戏的本身,就有很多天然的坑”。
昨天有朋友和我说:“最近有个叫《羊了个羊》的游戏爆火,就是太难玩了,你能复刻一个不?”话说上次玩休闲游戏还是在几年前,但是朋友之托必须赴汤蹈火啊,二话不说,开整!然而,冲动是魔鬼,直到此时此刻,老王也没能亲手玩一局原版游戏,不知道是游戏入口设计得太隐蔽还是网络加载太慢,无论手机端还是PC端,游戏都停留在如下界面。
所以本次游戏的复刻,完全是基于各视频网站云观摩的结果,好在游戏的玩法不是特别难理解。复刻使用的开发工具是Godot Engine(使用其它工具开发原理也是相似的),目前项目已经开源到了GitCode:Godot版《羊了个羊https://gitcode.net/hello_tute/SheepASheep
接下来我将通过临摹游戏的方式推测一下这个小游戏的实现原理,本文主要面向对游戏开发有兴趣的朋友,欢迎大家多提宝贵意见。
程序员用12小时复刻《羊了个羊》,代码已开源!

先说说玩法

第一眼看到《羊了个羊》,老王首先想到当年的《连连看》,不过有网友爆料,该游戏“借鉴”了《3tiles》。瞄了眼3tiles,是比较相似。说心里话,这个游戏的玩法并没有什么过于出众的地方,算是个中规中矩的“低卡路里”休闲游戏。
之所以成为话题作品,主要就是因为它的第2关极其低的通关率,一下子激起了众多玩家的挑战欲望。而时至今日这个“低通关率”也被网络上的众多玩家揭秘,第2关其实大概率上本身就是个死局。是程序员故意挖坑设了死局么?先卖个关子,我们先聊聊游戏的开发,然后您自己就会有答案了。
程序员用12小时复刻《羊了个羊》,代码已开源!

实现概要

游戏的整体很简单,但其中有几个实现的重点需要注意:
  • 牌堆数据结构的实现
  • 如何检测和更新可拾取的牌
先做个小定义,一个牌堆中可被拾取的牌以下将简称其为:“窗口牌”。

牌堆的结构及其数据结构

最初,我还真被这复杂的牌堆结构蒙住了,但仔细研究一番发现,无论多么复杂的牌堆,其实都是由如下三种牌堆模式组合拼凑而成的。
  • 蓝圈圈出的牌堆模式A:上面1张牌只挡住下面1张牌;同时下面的牌仅被上面1张牌挡住。只要上面的1张牌被取走,下面的牌就成为窗口牌;
  • 红圈圈出的牌堆模式C:上面1张牌可以挡住下面4张牌;同时下面的牌可能被上面4张牌挡住,一张牌只有它上面的4张牌都被取走,它自己才成为窗口牌。
虽然上图中体现不是很明显,但不难猜想出,第三种牌堆模式B 的存在,那就是:
  • 上面1张牌可以挡住下面2张牌;同时下面的牌可能被上面2张牌挡住,一张牌只有它上面的2张牌都被取走,它自己才成为窗口牌。
对于牌堆模式A,有些朋友会迫不及待地用“队列”或“栈”实现它,这样做有两个缺点:
  • 逻辑上牌堆模式A的窗口牌也可能是2维的,如果用队列实现就限制了它的灵活性;
  • 牌堆模式B和C都不好用队列实现,所以想追求数据结构的统一,还要另求他法。
实际上无论牌堆模式A、B还是C,都不过是3维数组结构,上图中模式A看起来特殊,无非是它的x,y维度都为1罢了。而三种牌堆的区别也无非就是当一张窗口牌被取走,检查牌堆是否出现新的窗口牌的方法罢了。
牌堆模式A
程序员用12小时复刻《羊了个羊》,代码已开源!
牌堆模式B
程序员用12小时复刻《羊了个羊》,代码已开源!
牌堆模式C
程序员用12小时复刻《羊了个羊》,代码已开源!
牌堆的数据结构
我将其定义为MContainerBase基类
#MContainerBaseextends Node2Dclass_name MContainerBase
func _ready(): add_to_group(name) add_to_group("game") var Mask = FileReader.read(mask_file,null) box.resize(size_x) for i in range(size_x): box[i] = [] box[i].resize(size_y) for j in range(size_y): box[i][j] = [] box[i][j].resize(size_z) for k in range(size_z): if Mask == null or Mask[i][j] == 1: box[i][j][k] = add_tile(i,j,k,get_parent().distribute_face()) else: box[i][j][k] = null for x in range(size_x): for y in range(size_y): for z in range(size_z): check_is_on_top(x,y,z)
最基础的牌堆就是一个 x*y*z的三维数组,我们可以使用一切方法构造想要的排队形状:柱形、条形、甚至金字塔形。这都不会影响后面程序的实现。
项目中为了增加这个“大方块”的多样性,我还给它设置了如下的“遮罩”,这就是游戏中CSDN文字的由来。当然我们还可以通过“遮罩”来自由定义窗口牌,这部分就请大家自由发挥了。
# S形遮罩[ [0,0,0,0,0], [0,0,0,0,0], [1,1,1,0,1], [1,0,1,0,1], [1,0,1,1,1],]
程序员用12小时复刻《羊了个羊》,代码已开源!

如何检测和更新可拾取的牌

三种牌堆模式分别派生自MContainerBase,并对应着如下三种检测方式:
牌堆模式A
仅检测自己正上方是否有牌
#1 Cover 1extends MContainerBase
func check_is_on_top(x,y,z): if has_tile(x,y,z): if not has_tile(x,y,z + 1) : (box[x][y][z] as MTile).set_is_on_top(true)
牌堆模式B
检测自己上方两方位是否有牌
#1 Cover 2extends MContainerBase
func check_is_on_top(x,y,z): if has_tile(x,y,z): if z%2 == 0: if not has_tile(x,y,z + 1) and not has_tile(x - 1 ,y,z + 1): (box[x][y][z] as MTile).set_is_on_top(true) else: if not has_tile(x,y,z + 1) and not has_tile(x + 1 ,y,z + 1): (box[x][y][z] as MTile).set_is_on_top(true)
牌堆模式C
检测自己上方四方位是否有牌
#1 Cover 4extends MContainerBase
func check_is_on_top(x,y,z): if has_tile(x,y,z): if z%2 == 0: if not has_tile(x,y,z + 1) and not has_tile(x - 1 ,y,z + 1) and not has_tile(x,y - 1 ,z + 1) and not has_tile(x - 1,y - 1,z + 1): (box[x][y][z] as MTile).set_is_on_top(true) else: if not has_tile(x,y,z + 1) and not has_tile(x + 1 ,y,z + 1) and not has_tile(x,y + 1 ,z + 1) and not has_tile(x + 1,y + 1,z + 1): (box[x][y][z] as MTile).set_is_on_top(true)
在Godot中,这三种牌堆模式还可以通过场景节点制作成预制体,这样关卡设计师就可以轻松地制作出美观的关卡了。
程序员用12小时复刻《羊了个羊》,代码已开源!
程序员用12小时复刻《羊了个羊》,代码已开源!
程序员用12小时复刻《羊了个羊》,代码已开源!

如何生成新关卡

简单了解游戏规则后,我们就不难推导出,每个关卡能被通过的一个必要条件就是每一种图案的总数,必须能被3整除。实现方法如下:
var tiles = []export var initial_tiles = { 0:10, 1:10, 2:10, 3:10, 4:10, 5:10, 6:10, 7:10, 8:10, 9:10, 10:10, 11:10, 12:10, 13:10, 14:10, 15:10}
func _init(): for key in initial_tiles: var num = initial_tiles[key]*3 for i in range(0,num): tiles.append(key) tiles.shuffle()
其中字典initial_tiles 的key对应着每一种图案,后面的value对应着这一关该图案出现的“对数”(此处1对等于3个)。按照value乘以3的数量存入数组tiles(下文称之为:待发牌池),然后把待发牌池中的元素打乱顺序,等待“发牌”。

关于游戏中的坑

很多朋友抱怨:“程序员故意挖坑制作死关卡”。其实不然,他无须故意挖坑,因为这个游戏本身就有很多“天然的坑”,如果不使劲填坑,它们自然而然就属于你了。而这里就隐藏了几个可致命的坑:乍一看,待发牌池中所有的图案都可以被3整除那么一定可以通关?那可不一定:
  • 只有桌面牌堆中牌的数量和待发牌池牌数一致,所有的牌才能“落地”,而游戏中桌面牌堆到底有多少(层)本身就是个迷。并且如果没猜错的话,在每一局设计者先要确保牌堆形状好看,然后再使堆牌数和待发池的牌数一致。二者哪怕差1个,也会造成死局。
  • 上文说了,桌面牌数和待发牌池的牌数一致只是过关的必要而非充分条件。即使该条件满足,如果相对于牌桌上的牌数以及图案数量,窗口牌数太少,也会造成死局。比如下面这个极端的例子:假设游戏共有 15种花色,而牌桌上只有这个模式A牌堆,它有90张牌。那么玩家只要在连续7次拾牌时没有遇到3个相同图案的牌,就“必死无疑”了。
程序员用12小时复刻《羊了个羊》,代码已开源!
其实这个游戏,一方面要控制关卡的难度,另一方面又要保证能通关本身就是一个相当困难的问题(至少老王没有想出办法)。而设计者反其道而行之,(可能)没有花力气去设计算法,把坑留给玩家,得到了极低的通关率,反而制造了话题并形成爆款。如此说来,这确实是个抖机灵的“设计”。但老王认为这种“设计”在游戏策划中是不宜被借鉴的,就像现在市面上泛滥的悬疑剧,开始埋坑无数,吊足观众胃口,最后烂尾不了了之一样,长此以往观众(玩家)对于悬疑剧(游戏)的信任感就被消费殆尽了。

洗牌道具的实现

洗牌的实现原理很简单,把当前桌面的牌记录在一个数组tiles中,当需要洗牌时,先打乱一下数组中牌的顺序,然后让桌面上每一张牌到tiles中重新取一个值。再来个眼花缭乱点的动画,还真挺像那么回事儿。
程序员用12小时复刻《羊了个羊》,代码已开源!
func shuffle_tiles(): tiles.shuffle() tiles_index = -1 func redistribute_face() -> int: tiles_index += 1 return tiles[tiles_index]

遮罩文件的读取

这里要夸一下Godot Engine,它的很多功能真是方便,比如下面这个str2var它可以简单粗暴地直接把字符串转换成对象类型。
class_name FileReader
static func read(path,default_data): var data = default_data var file = File.new() file.open(path,File.READ) var content :String = file.get_as_text() if not content.empty(): data = str2var(content) file.close() return data

对象间的通信

这个小游戏中存在大量的对象间的通信需求:牌和牌之间、牌和牌堆之间、牌和关卡之间、牌堆和关卡之间。为了快速实现游戏,我大量使用了Godot Engine的Group机制,不得不说Group是Godot Engine最赞的设计之一。
程序员用12小时复刻《羊了个羊》,代码已开源!
程序员用12小时复刻《羊了个羊》,代码已开源!
程序员用12小时复刻《羊了个羊》,代码已开源!

总结

小游戏《羊了个羊》,从策划和开发的角度来看并不困难,然而“瑕疵”竟然能够成为“噱头”,也让人不得不感慨“游戏世界真的一切皆有可能啊”。
※本文内容来自授权转载,不代表TechWeb观点,转载请联系CSDN(ID:CSDNnews)
 
—【 THE END 】—
转自:https://mp.weixin.qq.com/s/ELyL8PsVxqFMnRgDbsUCcg

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

某东抢茅台脚本在 GitHub 连续霸榜

12 月 30 日,前哨君在刷 GitHub 的时候,看到热榜上有一个项目jd_seckill,是在某东上抢茅台的 Python 脚本。

这个脚本主要功能为:“自动登录某东 -> 定时自动预约茅台 -> 定时开始自动抢购。目前仅支持在某电商平台上抢购飞天茅台,暂不支持其他功能。”

前哨君听朋友说,茅台飞天酒倒一手能获利上千。不得不说,随着茅台的热热,这个抢购脚本软件的 Star 数也是一路飙升。

31 日,这个项目飙升到榜首,一天狂揽 2000 多 Star。截止元旦,该项目已有 7400+ Star。

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

不过在 31 日傍晚,jd_seckill的开发者已经清空仓库了。

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

不过由于该仓库 fork 数量非常多,已经在 GitHub 上扩散开了。有其他程序员另外创建仓库了。

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub
3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

main.py 代码

import sys
from jd_spider_requests import JdSeckill


if __name__ == '__main__':
    a = """
       oooo oooooooooo.            .oooooo..o                     oooo         o8o  oooo  oooo  
       `888 `888'   `Y8b          d8P'    `Y8                     `888         `"'  `888  `888  
        888  888      888         Y88bo.       .ooooo.   .ooooo.   888  oooo  oooo   888   888  
        888  888      888          `"Y8888o.  d88' `88b d88' `"Y8  888 .8P'   `888   888   888  
        888  888      888 8888888      `"Y88b 888ooo888 888        888888.     888   888   888  
        888  888     d88'         oo     .d8P 888    .o 888   .o8  888 `88b.   888   888   888  
    .o. 88P o888bood8P'           8""88888P'  `Y8bod8P' `Y8bod8P' o888o o888o o888o o888o o888o 
    `Y888P                                                                                                                                                  
                                               
功能列表:                                                                                
 1.预约商品
 2.秒杀抢购商品
    """

    print(a)

    jd_seckill = JdSeckill()
    choice_function = input('请选择:')
    if choice_function == '1':
        jd_seckill.reserve()
    elif choice_function == '2':
        jd_seckill.seckill_by_proc_pool()
    else:
        print('没有此功能')
        sys.exit(1)

1 月 1 日,优化后的仓库,也冲上 GitHub 热榜,并且排第二。

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

对完整代码感兴趣,请参见:

https://github.com/ChinaVolvocars/jd_maotai_seckill

某猫和某宝的抢茅台脚本

虽然jd_seckill被作者删了,31 日 GitHub 热榜上又来了一个抢茅台的。

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

//定时器
var timer = null;

//检测状态
function checkElementState(path,callback){
 var ele = document.querySelector(path);
 if(ele){
  callback && callback();
 }else{
  console.log('异步加载元素中....' + path );
  setTimeout( function(){checkElementState(path,callback);},200);
 }
}



//点击购买按钮
function clickBuy(){
 
 console.log('买!');
 
 //票的数量  如果还不可以购买,这个地方获取会失败 
 var amount = document.getElementsByClassName('mui-amount-increase')[0];
 amount && amount.click();  //+1
 
 var btnBuy = document.querySelector('');
 
}


//结算
function checkOut(){
 
 
 console.log('结算开始....');
 var btn = document.getElementById('J_Go');
 
 if(btn){
  btn.click();
 }else{
  console.log('结算按钮没找到');
 }
 
}

function checkOutAsync(){
 checkElementState('#J_Go',checkOut);
}

//提交订单
function submitOrder(){
 
 console.log('提交订单开始....');
 
 
 
 checkElementState('.go-btn',function(){
  var btn = document.querySelector(".go-btn");
 
  if(btn){
   btn.click();
  }else{
   console.log('提交订单按钮没找到');
  }
   
 });
}



//目标时间
 var dDate = new Date();  //10点和20点开抢
 if( dDate.getHours() < 10 ){
  dDate.setHours(9,59,59.2);
 }else{
  dDate.setHours(19,59,59.2);
 }
 
 //dDate.setSeconds( dDate.getSeconds() + 10 );
 
//进入时间判断循环
function enterTimeCheckLoop(callback){
 var date = new Date();
 
 
 
 
 var diff = Date.parse(dDate) - Date.parse(date) ;
 
 console.log(diff);
 
 if(diff < - 900 ){
  
  console.log('时间过了!');
  
 }else if(diff < 500 ) {

  callback && callback();
  
  console.log('时间到了!!!');
  
 }else{
  setTimeout(function(){ enterTimeCheckLoop(callback);},400);
  
  //console.log('--');
 }
 
 
}


//主要函数
function main(){
 console.log('############################开始抢购茅台############################');
 
 //debugger;
 
 var href = window.location.href;
 if(href.indexOf('cart.tmall.com') > -1 ){
  //结算页面
  
  //进入时间判断
  enterTimeCheckLoop( checkOutAsync );
 
 
 }else if(href.indexOf('buy.tmall.com') > -1 ){
  //提交订单页面
  
  submitOrder();
 }
 
}

main();

仓库地址:https://github.com/cehui0303/Tmall_Tickets

经过一天的时间,今天也冲上 GitHub 热榜第三

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

不过Tmall_Tickets这个仓库,最后修改时间是在 12 个月前。也就是一年没维护了,有童鞋已反馈没效果。

好消息是,又有一个针对某宝和某猫的 Python 抢购脚本,也登上了日榜。

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

3 天狂揽 7000 Star,抢茅台脚本刷榜 GitHub

仓库链接:https://github.com/jerry3747/taobao_seckill

不得不说,这连续几天的 GitHub 热榜“刮起了”茅台抢购风!

– EOF –

转自:https://mp.weixin.qq.com/s/K1VYmiJ2D2sia2mW4z0rsw

10 个有用的软件开发原则

作者 | Kevin Mahoney
来源 | 架构头条

我总结了一些软件开发原则。在这些原则中,大多数都是以简化系统为核心。在我看来,简单的系统会更可靠,更容易修改,而且一般更容易使用。当观念发生改变时,我希望更新它们。

1 剔除无效状态

我把这一点排第一,是因为我认为它是最重要、最强大的原则之一。

你可能在定义类型时听到过这个词,但其实这个原则适用于所有与表示数据相关的地方——例如数据库设计。

它不仅可以减少系统的状态数量(从而变得更简单),还能减少无效状态的数量!你的系统不需要处理这些无效状态,因为它们在你的程序中实际上是不可表示的。

这不只是一个小技巧,它可以极大简化你的系统,并防止出现各种类型的 bug。这有一些例子。

https://kevinmahoney.co.uk/articles/applying-misu/

2 数据一致性让系统更简单

对数据施加一致性规则,减少了系统需要处理的状态数量。这是从上一个原则派生而来的。

定义

这里说的是一致性的普遍含义:即数据遵循某些规则,并且在任意时刻都始终遵循这些规则。这一定义与 ACID 有关,但不要与 CAP 混淆起来了。

规则可以是任何东西,例如,你的信用永远不能变成负数,或者私密的帖子不应该被其他人看到。它不仅限于外键或惟一索引,尽管它们也是有效的规则。

和数据库一样,应用程序也可以通过使用 ACID 事务来加强一致性。如果能在数据库级别强制保持一致性是最好的,但在实际中,对稍微复杂一点的东西来说,这样做并不常见。

实用建议

任何限制或损害一致性的行为都会导致复杂性。这就引出了以下这些实用的建议:

让系统更简单:

  • 更少的数据库 (理想情况下是一个)

  • 规范化,减少冗余数据

  • 一个“好的”数据库设计

  • ACID 事务

  • 更多的数据约束

让系统更复杂:

  • 多个数据库

  • 冗余或非正规化数据

  • 糟糕的数据库设计

  • 较少(或没有)数据约束

当然,有时候让系统变复杂也是有正当理由的,我并不想让复杂性变成一个“肮脏的”词。请参阅后面的一个原则“杀鸡不要用牛刀”。

我认为这个原则是当今软件工程中最被低估的原则之一。一致性问题经常被忽视。很多问题,我敢说大多数问题,基本上都是一致性问题——数据不符合某些期望。

参见附录,了解不一致性是如何导致复杂性的。

https://kevinmahoney.co.uk/articles/my-principles-for-building-software/#appendix-a-inconsistency-results-in-complexity

3 数据设计先行

这个问题,“代码还是数据?”,哪一个在 10 年后更有可能继续存在。

代码可以被丢掉重写,但数据很少会这样。

数据比代码更重要。代码的唯一目的是转换数据。

在设计新系统时,最好先从数据库和数据结构开始,并在此基础上开发代码。要考虑可以在数据上施加的约束并实施它们,理想情况下是通过表示数据的方式进行的。

代码设计是数据设计的下一步。数据模型越简单、越一致,代码就会越简单。

你们把流程图给我看,但把表藏起来,我就一头雾水。你们把表给我看,通常我就不需要你们的流程图,它们会不言自明。—— Fred Brooks

糟糕的程序员关心代码。好的程序员关心数据结构和它们之间的关系。—— Linux 之父 Linus Torvalds

4 杀鸡不要用牛刀

这是软件开发人员最常犯的错误。

这个原则是说,当你在做需要付出复杂性代价的权衡时,要确保权衡的必要性得到经验证据的支持。

常见错误:

  • 试图构建一个复杂的“可伸缩”系统,可以伸缩到你可能永远都不需要的规模。

  • 在不考虑需求或成本的情况下,让服务尽可能地小。

  • 在非性能瓶颈的地方优化性能,增加不一致性或复杂性。

建议:

  • 尽可能从最简单、最正确的系统开始

  • 对性能进行度量

  • 如果不能解决实际问题,就不要付出复杂性代价或违反其他原则。

  • 有些优化可以不进行度量,因为它们的成本非常低或为零。例如,为了保证你想要执行的操作具有你想要的性能,使用正确的数据结构。

  • 的确,有时候经验本身就能告诉你是否做出了正确的权衡。但如果你能证明,那就更好了。

  • 当你必须做出选择时,请选择正确性和简单性,而不是性能。

  • 在某些情况下,正确而简单的代码是性能最好的代码!

真正的问题是程序员在错误的地方和错误的时间花了太多的时间在担心效率上。过早优化是编程中所有(或者至少是大部分)罪恶的根源。——计算机科学家 Donald Knuth

5 避免为了局部简单性而增加全局复杂性

也就是避免为了让系统的一部分变得更简单,而导致整个系统变得更复杂。

这种交换通常是不平等的。追求局部的简单性会导致全局复杂性的增加,而且是数量级的。

例如,使用较小的服务可以让这些服务变得更简单,但一致性的降低和对更多进程间通信的需求让系统变得更加复杂。

6 识别内在的复杂性

有时候事情本身就很复杂,你不能把问题简单化。

任何这样的尝试都只会让系统变得更加复杂。

7 使用的技术越少,系统就越简单

深入理解一小部分技术要比只是表面理解很多技术好。

更少的技术意味着更少的东西要学习和更少的运维复杂性。

8 集中精力学习概念,而不是技术

不要太关心技术的复杂细节,因为你可以随时查阅它们。你要学习底层的基本概念。

技术会变化,概念却是永恒的。你学到的概念将被用在更新的技术中,你就可以更快地学会新技术。

例如,不要太关注 React、Kubernetes、Haskell、Rust 的表面细节。

重点学习:

  • 纯函数式编程

  • 关系型模型

  • 规范的方法

  • 逻辑编程

  • 代数数据类型

  • 类型类 (通用的和特定的)

  • 借位检查器 (仿射 / 线性类型)

  • 依赖类型

  • Curry-Howard 同构

  • 同像性(Homoiconicity)

  • VirtualDOM

  • 线性回归

  • ……

9 代码一致性很重要

有时候,具有一致性的代码比“正确”的代码更重要。如果你想要改变代码库中某些代码的行为,就要修改它所有的实例。否则的话,就只能忍受。

代码的可读性更多地与一致性(而不是简单性)有关。人们通过模式识别来理解代码,所以请重复 (和记录) 模式!

10 分享原则很重要

如果你和队友之间的共同原则越多,就能越好地在一起工作,而且你会越喜欢和他们在一起工作。

11 附录:不一致性导致的复杂性

这是我能想到的最简单的例子,希望能毫不费力地与现实问题联系起来。

假设一个数据库有两个布尔变量 x 和 y,你的应用程序有一个规则,即 x = y,可以通过使用一个事务修改这两个变量来执行这个规则。

如果这个规则被正确执行,那么数据只有两种状态:(x = True,y = True) 或 (x = False,y = False)。

基于这个规则的函数“toggle”就非常简单。你可以读取其中一个值,并将两个值都设置为反向值。

现在,假设你将这两个变量放到不同的数据库中,并且不能再被一起修改,那么会发生什么?

因为你不能确保 x = y 的一致性,所以数据可以有两种以上的状态:(x = True,y = False) 或 (x = False,y = True)。

  • 如果你的系统处于这些状态中的一种,你应该使用哪个值?

  • 当处于其中的一种状态时,“toggle”函数的行为是怎样的?

  • 在写入新值时,如何确保两次写入都成功?

这些问题没有正确的答案。

当然,如果我们一开始就遵循“剔除无效状态”的原则,那么将只有一个变量!

原文链接:https://kevinmahoney.co.uk/articles/my-principles-for-building-software/

我在Github上找到开源的《植物大战僵尸》

来自:开源最前线(ID:OpenSourceTop) 
项目地址:https://github.com/marblexu/PythonPlantsVsZombies
“歪比歪比?歪比巴卜!”

《植物大战僵尸》这个音效不知道是多少人的童年,这款游戏诞生已经十几年了,《植物大战僵尸》这款游戏还是频频出现在各路主播的屏幕上,不厌其烦。
我在Github上找到开源的《植物大战僵尸》
最近,猿妹在Github上看到有人用Python实现了《植物大战僵尸》游戏,并开源了,这个项目使用 Python 将这个塔防游戏重新复现了一遍,尽管暂时没有把所有要素都复现出来——目前只实现了部分植物和僵尸以及白天黑夜关卡而已,但足够勾起一波回忆杀。
PythonPlantsVsZombies功能实现如下:
  • 支持的植物类型:太阳花,豌豆射手,寒冰射手,坚果,樱桃炸弹。新增加植物:双重豌豆射手,三重豌豆射手,食人花 ,小喷菇,土豆地雷,倭瓜。

  • 支持的僵尸类型:普通僵尸,旗子僵尸,路障僵尸,铁桶僵尸。新增加读报僵尸。

  • 使用json文件保存关卡信息,设置僵尸出现的时间和位置。

我在Github上找到开源的《植物大战僵尸》

如果想尝试一下这个游戏,你首先需要安装Python 3.7,然后克隆项目源代码,最后启动项目,就可以愉快的玩耍了~~
游戏演示界面如下:
 

我在Github上找到开源的《植物大战僵尸》

我在Github上找到开源的《植物大战僵尸》

除了玩游戏,如果你是一个Python初学者,并且对小游戏编程感兴趣,通过这个项目你可以学到许多面向对象程序设计知识。

我在Github上找到开源的《植物大战僵尸》

目前,PythonPlantsVsZombies在Github上标星 1.4K,累计分支 313 个(Github地址:https://github.com/marblexu/PythonPlantsVsZombies

红色警戒游戏源代码被开源了!

 

来自:GitHub中文社区 

 作者:huber

 

近日,知名游戏公司 EA 在 GitHub 上搞了个大新闻,把《命令与征服》系列中的 2 个游戏的部分源码开源了!其中的 Red Alert(红色警戒)相信大家都很熟悉了

 

这一提到红警,记忆被瞬间拉到了学生时代,这个游戏也是huber我接触的第一个单机游戏,记得当时不吃饭也要省钱偷偷去网吧,玩红警能玩一个通宵😂

 

咱们今天就来聊聊关于这次红警开源的一些事

 

真香!红色警戒游戏源代码被开源了!

 

因为红警1的启动程序名为RA95.exe,所以在国内基本都叫它红警95,而目前开源的,只是红警95,而红警2估计永远不会开源了,因为当年在EA收购开发红警系列的Westwood时,不慎弄丢了红警2的源代码,所以……

 

 

真香!红色警戒游戏源代码被开源了!

 

 

这才开源了几天,目前已经收获 star 8k 多,可见这款游戏是多少人的青春啊

 

 

真香!红色警戒游戏源代码被开源了!

 

 

真香!红色警戒游戏源代码被开源了!
红色警戒截图

 

开源后,issues中不少人在讨论、回忆,其中一个中国哥们贴了一段中英文配音:

 

 

真香!红色警戒游戏源代码被开源了!

 

 

看着后面英文口号,各种游戏画面已经不由自主在脑海里了,每一句都是经典,每一句都印记在童年的脑海里啊!简直要泪奔啊哈哈哈😭😭,当年和你一起玩红警的小伙伴们,可还记得?反正我是连他们的名字都忘了……

 

 

真香!红色警戒游戏源代码被开源了!
红色警戒截图

 

然而从开源到现在,大家感叹最多的,居然是:这上世纪的代码,竟然如此整洁!注释如此清晰!

 

于是忍不住去看了一下这个上世纪的c++代码,感觉真是开源界的一股清流啊:

 

 

真香!红色警戒游戏源代码被开源了!

 

 

这样整洁的代码,这样详细的注释,在所有代码文件里都能看到!

 

 

真香!红色警戒游戏源代码被开源了!

真香!红色警戒游戏源代码被开源了!

 

 

就连一个最简单的函数,都要写一大段注释:

 

 

真香!红色警戒游戏源代码被开源了!

 

忍不住反省了一下,现在还有多少人,有多少项目,能做到这一点?

 

真香!红色警戒游戏源代码被开源了!

 

这样的代码风格规范,应该是所有程序员,所有工程的楷模,值得大家学习!

 

 

那对于开发者来说,这个游戏开源的意义有哪些呢? 可以总结为3大点:

 

1,学习,对于刚进入游戏领域的童鞋,这是一个非常不错的学习项目

2,代码、资源二次利用,部分资源,代码是可以直接拿来使用,毕竟注释这么清晰,代码这么整洁的资源,复用起来会轻松很多

3,玩家可创建自定义单位、修改游戏逻辑以及数据,可以无边界的去体验这个游戏

 

传送门

https://github.com/electronicarts/CnC_Remastered_Collection

 

 

END

转自:https://mp.weixin.qq.com/s/xxCnBt4aDs92sBPYl5Ea-A