新年又到了,小编祝大家:兔飞猛进、钱兔无量、大展宏兔、兔年吉祥!
过年亲人们总是要合家团聚的,一起开心快乐度过这个喜气洋洋的春节。
相比看着无聊的春晚,大大小小的朋友们更愿意做一些精彩的、有意思的事情。何况初一初二很多时候是没有安排节目的,那就要在家呆呆的呆着。
这里给大家分享12款可以一家人一起玩的同屏联机游戏(支持本地4人及以上游玩),希望能给大家在春节带去一些充实与欢乐。
当然这是我直到或者玩过的一些游戏,还有很多其他好玩的,请大家留言分享一下。
bing搜索 好玩游戏厅 可获取游戏。
1《马里奥赛车8 豪华中文版》/Mario Kart 8支持1-4人游玩
《马里奥赛车8》是switch独占的经典赛车游戏。
游戏上手简单但是上限高,有着四十多位任天堂的游戏角色可供选择,并且支持赛车多部件的DIY选择。
游戏分为竞速、道具、和其他小游戏模式。其中道具赛趣味性最高,完全随机的道具大大增加了游戏的随机性。即便遥遥领先,也得时刻提防身后飞来的道具。即使落后了也不要紧,拿好道具、瞅准机会进行一个绝地翻盘也不是梦。
本地最多支持4人分屏游玩,喜欢赛车的小伙伴们不妨来试试。
2《新超级马里奥兄弟U豪华版》New Super Mario Bros. U支持1-4人游玩
该游戏是经典的硬核横板闯关游戏,地图设计十分有趣,隐藏合理且构思巧妙,在游戏中不仅仅能享受多人协作闯关的乐趣还能感受关卡设计师的神奇脑洞 。并且该作还增加了新角色——偷天兔(免疫绝大部分伤害)。帮助玩家度过一些较难的关卡,防止游戏进度被卡而失去耐心。横板闯关游戏爱好者不要错过了。
在合作模式下,只要有一名玩家没有阵亡都可以复活另一名玩家继续游戏。
3《超级马里奥派对》SUPER MARIO PARTY支持1-4人游玩
如果没有体感手柄。请百度搜索 “YUZU模拟器手机体感软件MotionSource使用教程(安卓)”。可以使用安卓手机模拟。
NS上多人聚会必玩的一款游戏,内置80个妙趣横生的小游戏。
游戏不仅有对抗玩法,还有合作玩法。不同的小游戏会接连的随机出现,一直玩一直快乐。打开《马里奥派对》就是打开了快乐开关。
如果你还没有一款中意的多人游戏,那么《马里奥派对》请务必和亲友一起试试。
4《胡闹厨房》1、2 Overcooked支持1-4人游玩
胡闹厨房(分手厨房)是一款多人合作的做菜游戏。玩家需要和队友在各种奇形怪状的地图里通过切菜、炸菜、煮饭、洗碗等分工合作,完成客人的订单,挑战高薪。
游戏有任务目标和时间限制,具体怎么做就要看玩家如何分配了。如果合作顺利就是专业团队,如果合作不好就是厨房乱斗。脾气不好的慎玩儿。
5《胡闹搬家》Moving Out支持1-4人游玩
如果觉得《胡闹厨房》太难,不妨来试试《胡闹搬家》。
只需要和小伙伴进屋拿货,然后把他们通通丢到车里。在规定时间内把对应数量的家具装车就算完成任务。
在搬运小件货的时候可以单人完成,也可以和队友完成丢货接力来加快搬货速度。遇到奇形怪状的大件货,就需要和小伙伴配合搬运,只要节奏配合的好,大件货也可以高抛上车。
在不同的地图里还有机关、道具,加快或者阻拦玩家搬货。如果碰到捣乱的NPC,就需要一个兜将其制服才能顺利完成搬运。说实话我从来没有这么喜欢过搬家。
6《任天堂明星大乱斗:特别版》Super Smash Bros Ultimate支持1-8人游玩
《任天堂大乱斗》有着将近100位任天堂第一方及第三方的游戏角色,100多个对战场地,800多首BGM,每个角色的招式都不尽相同,这么多角色总能找到适合你的。
游戏属于易上手难精通的类型,任天堂角色的画风都比较容易让人接受。如果想玩格斗类的游戏,又不想太过严肃,那不妨和亲友们试试任天堂大乱斗吧。
7《忍者神龟:施莱德的复仇》Teenage Mutant Ninja Turtles: Shredder’s Revenge支持1-6人游玩
忍者神龟系列横版街机玩法的最新制作,原汁原味的玩法。
游戏有16个关卡,两个小时左右即可通关。
游戏难度不高,单人游玩也绰绰有余。如果说单人游玩的目的是活着通关,那么六个人玩就是龟龟去哪儿了。人越多场面越混乱,趣味性就越高。
游戏按当今的眼光来看属于中规中矩,但架不住和小伙伴一起玩,快乐才是最重要的!
8《随动回旋镖》Boomerang Fu支持1-6人游玩
玩家将在这款游戏中将各种好吃的作为回旋镖、道具、机关,把其他玩家切片或是打扁。游戏有几十张地图和不同的道具机关可以使用。
游戏画风清新可爱,操作简单,上手即玩。即便是游戏新手,也可以快乐玩耍。玩的人越多越刺激有趣。
9《雷曼传奇》Rayman® Legends支持1-4人游玩
一款育碧出品的横版过关游戏,包含超过60个2D关卡。游戏的故事模式支持最大4人合作通关。
在游戏中玩家可以解锁新的技能,并使用新技能重新挑战之前的关卡,以重新发现新的道路和隐藏道具等。
10《百战天虫WMD》Worms W.M.D支持1-4人游玩
本作是经典的策略动作游戏----百战天虫系列重启之作。
游戏采用全新2D卡通风格的虫子设定和华丽的手绘背景地图。
玩家将首次有机会操作载具,甚至进入建筑。在传统的炮火射击游戏基础上增加新的策略要素。
11《分手装修》Tools Up!支持1-4人游玩
《Tools Up》和《胡闹厨房》非常类似,同为最多4人同玩,讲究分工合作的派对类游戏。
玩家将化身成超级肥胖的装修师傅,在房间里刷油漆、贴墙纸、砌地板、铺地毯等等。每位玩家都需要出力合作,务求在最短的时间内做好装修的每项要求。
12.《大富翁11》RichMan 11支持1-4人游玩
各个角色搞笑又各具特点的配音台词,勾起了很多我的回忆。无论是台湾腔的普通话,闽南语还是日文,都给整个游戏玩起来增色不少。这系列的游戏本就不以画面见长,这些有小心思的设计,反而让人觉得有意思。
这个游戏本来就是为聚会而生,如果和3个电脑AI或者陌生人对战,输赢不论,乐趣就少了非常多。这类游戏本就深度不高,就是要和朋友一起玩才有更多乐趣。这运气成分极大,本就是欢乐为主,技巧性不够,胜负也不是很稳定。但是没有比寻回那份童年记忆中的欢乐更令人慰藉的。
最后祝大家春节快乐。
----由好玩游戏厅coarcade网站整理。
这是探讨 Go 编译器两篇文章的最后一篇。在 第 1 部分中,我们通过构建自定义的编译器,向 Go 语言添加了一条新语句。为此,我们按照此图介绍了编译器的前五个阶段:
go compiler flow
在"rewrite AST"阶段前,我们实现了 until 到 for 的转换;具体来说,在gc/walk.go[1]文件中,在编译器进行 SSA 转换和代码生成之前,就已进行了类似的转换。
在这一部分中,我们将通过在编译流程中处理新的 until 关键字来覆盖编译器的剩余阶段。
SSA在 GC 运行 walk 变换后,它调用 buildssa(gc/ssa.go[2])函数将 AST 转换为静态单赋值(SSA)形式[3]的中间表示。
SSA 是什么意思,为什么编译器会这样做?让我们从第一个问题开始;我建议阅读上面链接的 SSA 维基百科页面和其他资源,但这里一个快速说明。
静态单赋值意味着 IR 中分配的每个变量仅分配一次。考虑以下伪 IR:
x = 1y = 7// do stuff with x and yx = yy = func()// do more stuff with x and y
这不是 SSA,因为名称 x 和 y 被分配了多次。如果将此代码片段转换为 SSA,我们可能会得到类似以下内容:
x = 1y = 7// do stuff with x and yx_1 = yy_1 = func()// do more stuff with x_1 and y_1
注意每个赋值如何得到唯一的变量名。当 x 重新分配了另一个值时,将创建一个新名称 x_1。你可能想知道这在一般情况下是如何工作的……像这样的代码会发生什么:
x = 1if condition: x = 2use(x)
如果我们简单地将第二次赋值重命名为 x_1 = 2,那么 use 呢?x 或 x_1 或...呢?为了处理这一重要情况,SSA 形式的 IR 具有特殊的 phi(originally phony)功能,以根据其来自哪个代码路径来选择一个值。它看起来是这样的:
ssa phi
编译器使用此 phi 节点来维护 SSA,同时分析和优化此类 IR,并在以后的阶段用实际的机器代码代替。
SSA 名称的静态部分起着与静态类型类似的作用;这意味着在查看源代码时(在编译时或静态时),每个名称的分配都是唯一的,而它可以在运行时发生多次。如果上面显示的代码片段是在一个循环中,那么实际的 x_1 = 2 的赋值可能会发生多次。
现在我们对 SSA 是什么有了基本的了解,接下来的问题是为什么。
优化是编译器后端的重要组成部分[1[4]],并且通常对后端进行结构化以促进有效和高效的优化。再次查看此代码段:
x = 1if condition: x = 2use(x)
假设编译器想要运行一个非常常见的优化——常量传播;也就是说,它想要在 x = 1 的赋值后,将所有的 x 替换为 1。这会怎么样呢?它不能只找到赋值后对 x 的所有引用,因为 x 可以重写为其他内容(例如我们的例子)。
考虑以下代码片段:
z = x + y
一般情况下,编译器必须执行数据流分析才能找到:
x 和 y 指的是哪个定义?存在控制语句情况下,这并不容易,并且还需要进行优势分析(dominance analysis)。在此定义之后使用 z 时,同样具有挑战性。就时间和空间而言,这种分析的创建和维护成本很高。此外,它必须在每次优化之后重新运行它(至少一部分)。
SSA 提供了一个很好的选择。如果 z = x + y 在 SSA 中,我们立即知道 x 和 y 所引用的定义(只能有一个),并且我们立即知道在哪里使用 z(在这个语句之后对 z 的所有引用)。在 SSA 中,用法和定义都在 IR 中进行了编码,并且优化不会违反不变性。
Go 编译器中的 SSA我们继续描述 Go 编译器中如何构造和使用 SSA。SSA 是 Go 的一个相对较新的功能[5]。除了将 AST 转换为 SSA 的大量代码(gc/ssa.go[6]),其它大部分代码都位于ssa[7]目录中,ssa 目录中的 README 文件是对 Go SSA 的非常有用的说明,请阅读一下!
Go SSA 实现还拥有我见过的一些最好的编译器工具(已经在编译器上工作了很多年)。通过设置 GOSSAFUNC 环境变量,我们将获得一个 HTML 页面,其中包含所有编译阶段以及每个编译阶段之后的 IR,因此我们可以轻松地检索出需要进行哪些优化。额外的设置可以将控制流程图绘制成 SVG。
让我们研究一下从 AST 为该以下代码段创建的初始 SSA:
func usefor() { i := 4 for !(i == 0) { i-- sayhi() }}func sayhi() { fmt.Println("Hello, for!")}
我将移除打印输出函数的原因是为了使输出的 SSA 更简洁。使用-l 进行编译以禁用内联,这将导致对 sayhi()的微小调用(由于常量字符串而生成更多的代码,对 fmt.Println()[2[8]]的调用会生成更多代码)。
产生的 SSA 为:
b1: v1 (?) = InitMem <mem> v2 (?) = SP <uintptr> v3 (?) = SB <uintptr> v4 (?) = Const64 <int> [4] (i[int]) v6 (?) = Const64 <int> [0] v9 (?) = Const64 <int> [1] Plain → b2 (10) b2: ← b1 b4 v5 (10) = Phi <int> v4 v10 (i[int]) v14 (14) = Phi <mem> v1 v12 v7 (10) = Eq64 <bool> v5 v6 If v7 → b5 b3 (unlikely) (10) b3: ← b2 v8 (11) = Copy <int> v5 (i[int]) v10 (11) = Sub64 <int> v8 v9 (i[int]) v11 (12) = Copy <mem> v14 v12 (12) = StaticCall <mem> {"".sayhi} v11 Plain → b4 (12) b4: ← b3 Plain → b2 (10) b5: ← b2 v13 (14) = Copy <mem> v14 Ret v13
这里要注意的有趣部分是:
bN 是控制流图的基本块。Phi 节点是显式的。最有趣的是对 v5 的分配。这恰恰是分配给 i 的选择器;一条路径来自 V4(初始化),从另一个 v10(在 i--)内循环中。出于本练习的目的,请忽略带有的节点。Go 有一种有趣的方式来显式地在其 IR 中传播内存状态,在这篇文章中我们不讨论它。如果感兴趣,请参阅前面提到的 README 以了解更多详细信息。顺便说一句,这里的 for 循环正是我们想要将 until 语句转换成的形式。
将 until AST 节点转换为 SSA与往常一样,我们的代码将以 for 语句的处理为模型。首先,让我们从控制流程图开始应该如何寻找 until 语句:
until cfg
现在我们只需要在代码中构建这个 CFG。提醒:我们在第 1 部分[9]中添加的新 AST 节点类型为 OUNTIL。我们将在 gc/ssa.go 中的state.stmt[10]方法中添加一个新的分支语句,以将具有 OUNTIL 操作的 AST 节点转换为 SSA。case 块和注释的命名应使代码易于阅读,并与上面显示的 CFG 相关。
case OUNTIL: // OUNTIL: until Ninit; Left { Nbody } // cond (Left); body (Nbody) bCond := s.f.NewBlock(ssa.BlockPlain) bBody := s.f.NewBlock(ssa.BlockPlain) bEnd := s.f.NewBlock(ssa.BlockPlain) bBody.Pos = n.Pos // first, entry jump to the condition b := s.endBlock() b.AddEdgeTo(bCond) // generate code to test condition s.startBlock(bCond) if n.Left != nil { sndBranch(n.Left, bEnd, bBody, 1) } else { b := s.endBlock() b.Kind = ssa.BlockPlain b.AddEdgeTo(bBody) } // set up for continue/break in body prevContinue := sntinueTo prevBreak := s.breakTo sntinueTo = bCond s.breakTo = bEnd lab := sbeledNodes[n] if lab != nil { // labeled until loop labntinueTarget = bCond lab.breakTarget = bEnd } // generate body s.startBlock(bBody) s.stmtList(n.Nbody) // tear down continue/break sntinueTo = prevContinue s.breakTo = prevBreak if lab != nil { labntinueTarget = nil lab.breakTarget = nil } // done with body, goto cond if b := s.endBlock(); b != nil { b.AddEdgeTo(bCond) } s.startBlock(bEnd)
如果您想知道 n.Ninit 的处理位置——它在 switch 之前针对所有节点类型统一完成。
实际上,这是我们要做的全部工作,直到在编译器的最后阶段执行语句为止!如果我们运行编译器-像以前一样在此代码上转储 SSA:
func useuntil() { i := 4 until i == 0 { i-- sayhi() }}func sayhi() { fmt.Println("Hello, for!")}
正如预期的那样,我们将获得 SSA,该 SSA 在结构上等效于条件为否的 for 循环的 SSA 。
转换 SSA构造初始 SSA 之后,编译器会在 SSA IR 上执行以下较长的遍历过程:
执行优化将其降低到更接近机器代码的形式所有这些都可以在在 ssa/compile.go 中的passes[11]切片以及它们运行顺序的一些限制passOrder[12]切片中找到。这些优化对于现代编译器来说是相当标准的。降低由我们正在编译的特定体系结构的指令选择以及寄存器分配。
有关这些遍的更多详细信息,请参见SSA README[13]和这篇帖子[14],其中详细介绍了如何指定 SSA 优化规则。
生成机器码最后,编译器调用 genssa 函数(gc/ssa.go[15])从 SSA IR 发出机器代码。我们不必修改任何代码,因为 until 语句包含在编译器其他地方使用的构造块,我们才为之发出的 SSA-我们不添加新的指令类型,等等。
但是,研究的 useuntil 函数生成的机器代码对我们是有指导意义的。Go 有自己的具有历史根源的汇编语法[16]。我不会在这里讨论所有细节,但是以下是带注释的(带有#注释)程序集转储,应该相当容易。我删除了一些垃圾回收器的指令(PCDATA 和 FUNCDATA)以使输出变小。
""euntil STEXT size=76 args=0x0 locals=0x10 0x0000 00000 (useuntil.go:5) TEXT ""euntil(SB), ABIInternal, $16-0 # Function prologue 0x0000 00000 (useuntil.go:5) MOVQ (TLS), CX 0x0009 00009 (useuntil.go:5) CMPQ SP, 16(CX) 0x000d 00013 (useuntil.go:5) JLS 69 0x000f 00015 (useuntil.go:5) SUBQ $16, SP 0x0013 00019 (useuntil.go:5) MOVQ BP, 8(SP) 0x0018 00024 (useuntil.go:5) LEAQ 8(SP), BP # AX will be used to hold 'i', the loop counter; it's initialized # with the constant 4. Then, unconditional jump to the 'cond' block. 0x001d 00029 (useuntil.go:5) MOVL $4, AX 0x0022 00034 (useuntil.go:7) JMP 62 # The end block is here, it executes the function epilogue and returns. 0x0024 00036 (<unknown line number>) MOVQ 8(SP), BP 0x0029 00041 (<unknown line number>) ADDQ $16, SP 0x002d 00045 (<unknown line number>) RET # This is the loop body. AX is saved on the stack, so as to # avoid being clobbered by "sayhi" (this is the caller-saved # calling convention). Then "sayhi" is called. 0x002e 00046 (useuntil.go:7) MOVQ AX, "".i(SP) 0x0032 00050 (useuntil.go:9) CALL "".sayhi(SB) # Restore AX (i) from the stack and decrement it. 0x0037 00055 (useuntil.go:8) MOVQ "".i(SP), AX 0x003b 00059 (useuntil.go:8) DECQ AX # The cond block is here. AX == 0 is tested, and if it's true, jump to # the end block. Otherwise, it jumps to the loop body. 0x003e 00062 (useuntil.go:7) TESTQ AX, AX 0x0041 00065 (useuntil.go:7) JEQ 36 0x0043 00067 (useuntil.go:7) JMP 46 0x0045 00069 (useuntil.go:7) NOP 0x0045 00069 (useuntil.go:5) CALL runtime.morestack_noctxt(SB) 0x004a 00074 (useuntil.go:5) JMP 0
如果您注意的话,您可能已经注意到“cond”块移到了函数的末尾,而不是最初在 SSA 表示中的位置。是什么赋予的?
答案是,“loop rotate”遍历将在 SSA 的最末端运行。此遍历对块重新排序,以使主体直接流入 cond,从而避免每次迭代产生额外的跳跃。如果您有兴趣,请参阅ssa/looprotate.go[17]了解更多详细信息。
结论就是这样!在这两篇文章中,我们以两种不同的方式实现了一条新语句,从而知道了 Go 编译器的内部结构。当然,这只是冰山一角,但我希望它为您自己开始探索提供了一个良好的起点。
最后一点:我们在这里构建了一个可运行的编译器,但是 Go 工具都无法识别新的 until 关键字。不幸的是,此时 Go 工具使用了完全不同的路径来解析 Go 代码,并且没有与 Go 编译器本身共享此代码。我将在以后的文章中详细介绍如何使用工具处理 Go 代码。
附录-复制这些结果要重现我们到此为止的 Go 工具链的版本,您可以从第 1 部分开始 ,还原 walk.go 中的 AST 转换代码,然后添加上述的 AST 到 SSA 转换。或者,您也可以从我的 fork 中[18]获取adduntil2 分支[19]。
要获得所有 SSA 的 SSA,并在单个方便的 HTML 文件中传递代码生成,请在构建工具链后运行以下命令:
GOSSAFUNC=useuntil <src checkout>/bin/go tool compile -l useuntil.go
然后在浏览器中打开 ssa.html。如果您还想查看 CFG 的某些通行证,请在函数名后添加通行名,以:分隔。例如 GOSSAFUNC = useuntil:number_lines。
要获取汇编代码码,请运行:
<src checkout>/bin/go tool compile -l -S useuntil.go
[1] 我特别尝试避免在这些帖子中过多地讲“前端”和“后端”。这些术语是重载和不精确的,但通常前端是在构造 AST 之前发生的所有事情,而后端是在表示形式上更接近于机器而不是原始语言的阶段。当然,这在中间位置留有很多地方,并且 中间端也被广泛使用(尽管毫无意义)来描述中间发生的一切。
在大型和复杂的编译器中,您会听到有关“前端的后端”和“后端的前端”以及类似的带有“中间”的混搭的信息。
在 Go 中,情况不是很糟糕,并且边界已明确明确地确定。AST 在语法上接近输入语言,而 SSA 在语法上接近。从 AST 到 SSA 的转换非常适合作为 Go 编译器的前/后拆分。
[2] -S 告诉编译器将程序集源代码转储到 stdout;-l 禁用内联,这会通过内联 fmt.Println 的调用而使主循环有些模糊。
via: https://eli.thegreenplace/2019/go-compiler-internals-adding-a-new-statement-to-go-part-2/
作者:Eli Bendersky[20]译者:keob[21]校对:@unknwon[22]
本文由 GCTT[23] 原创编译,Go 中文网[24] 荣誉推出
参考资料[1]
gc/walk.go: https://github/golang/go/blob/master/src/cmd/compile/internal/gc/walk.go
[2]
gc/ssa.go: https://github/golang/go/blob/master/src/cmd/compile/internal/gc/ssa.go#L281
[3]
静态单赋值(SSA)形式: https://enpedia/wiki/Static_single_assignment_form
[4]
[1: #jump1
[5]
相对较新的功能: https://blog.golang/go1.7
[6]
gc/ssa.go: https://github/golang/go/blob/master/src/cmd/compile/internal/gc/ssa.go
[7]
ssa: https://github/golang/go/tree/master/src/cmd/compile/internal/ssa
[8]
[2: #jump2
[9]
第 1 部分: https://eli.thegreenplace/2019/go-compiler-internals-adding-a-new-statement-to-go-part-1/
[10]
state.stmt: https://github/golang/go/blob/master/src/cmd/compile/internal/gc/ssa.go#L1024
[11]
passes: /d/file/gt/2023-09/1pkcfjpxvsl.go /d/file/gt/2023-09/f1yqeneg5pf.go README: https://github/golang/go/blob/master/src/cmd/compile/internal/ssa/README.md
[14]
这篇帖子: https://quasilyte/blog/post/go_ssa_rules/
[15]
gc/ssa.go: https://github/golang/go/blob/master/src/cmd/compile/internal/gc/ssa.go#L5903
[16]
自己的具有历史根源的汇编语法: https://golang/doc/asm
[17]
ssa/looprotate.go: https://github/golang/go/blob/master/src/cmd/compile/internal/ssa/looprotate.go
[18]
我的 fork 中: https://github/eliben/go/tree/adduntil2
[19]
adduntil2 分支: https://github/eliben/go/tree/adduntil2
[20]
Eli Bendersky: https://eli.thegreenplace/
[21]
keob: https://github/keob
[22]
@unknwon: https://github/unknwon
[23]
GCTT: https://github/studygolang/GCTT
[24]
Go 中文网: /d/file/gt/2023-09/5mavg3yyy5r GameZ制作的开放世界魔性沙雕游戏《神奇之鸡(Techno Chicken)》上架Steam,玩家将扮演一只魔鬼鸡,杀人、骑猪和母鸡交配等等,游戏将于不久后发售,支持中文,敬请期待。
【官方介绍】
《神奇之鸡》是一款游戏,游戏中你要接受一个非常梦幻的任务,你需要披荆斩棘,消灭一切拦路虎。就这么简单。尽管如此,如果你想,你还可以:感受能够摧毁一切的强烈动感节奏;奔跑、破坏、攻击敌人并享受其中的乐趣;以种十分另类的方式控制你的对手;催眠并渗透到游戏角色的思想中;让你充满野性的小鸡变得更加狂野。
该游戏具有角色扮演模式和许多辅助任务,你可以扮演成良种鸡或者像鸭子一样凶悍的小鸡!一切由你决定!当然,不要忘了还有警察,他们会想方设法把你的鸡蛋做成蛋卷。但是,为何不试试神奇的魔丸呢?一旦你精力耗尽,无法应对警察执法.……就将魔丸直接放在喙中,那时你将立于不败之地!