Bot for Tetris和逆向工程动画。 第二届编程冠军赛的移动赛道分析


移动赛道获奖者

在过去的一年中,Yandex举办了两次在线编程锦标赛。 我们将继续发布第二届锦标赛的任务分析。 我们提醒您,为参与者提供了四个方面的知识:机器学习,前端,移动开发和后端。 这是针对移动开发人员的资格回合的讨论-通过资格考试的人中有121人参加了决赛。 任务是由Yandex.Browser和搜索门户的其他团队的专家发明的。 在该中心,我们将介绍遗传算法的主题,为Tetris编写一个bot,并使用适用于Android和iOS的不同方法来完成该任务。

A.电缆

发言者:谢尔盖·米亚斯尼科夫(Sergey Myasnikov)
所有语言Python 3.7.3和Python 2.7
时间限制4秒20秒
内存限制256兆字节
进入标准输入或input.txt
结论标准输出或output.txt
N台计算机被带到了新办公室。 附近的射电望远镜不允许使用Wi-Fi进行通讯,并且服务台必须使用LAN电缆来组织网络。 收集了角落里所有的电缆后,管理员设法连接了K对计算机。 经过漫长的一天的工作后,累了的服务台工作人员会向您寻求帮助-计算完成任务所需的预算。 因此,您有可以连接管理员的M对计算机的列表,以及每条电缆的成本。 开始工作时,您发现了成本计算系统中的一个错误-而不是将成本相加,而是将它们相乘-因此,逻辑值为4的电缆和值为3的电缆不是7,而是12。

要完成此任务,您需要计算足以构成连接网络的电缆成本,以使每台计算机直接或间接连接到每台计算机。

I / O格式,示例和注释

输入格式


第一行包含数字0 <N <10 6,0≤K <10 6,0≤M <10 6

接下来的K行包含数字对0 <u,v≤N-帮助中心已连接的计算机数。

接下来的M行包含三元组,三组分别为0 <u,v≤N和0 <p≤10 9-管理员可以使用电缆成本p连接的计算机数。

输出格式


打印足以建立连接网络的电缆的最低成本;如果无法建立完整的网络,则打印–1。

答案可能非常大,因此您需要以2 31-1为模输出。

例子1

进入结论
2 0 1
2 1 9
9

例子2

进入结论
5 0 6
1 3 1
2 4 8
1 4 1
1 2 10
4 1 10
5 3 4
32

例子3

进入结论
6 0 3
4 3 2
2 6 9
1 4 8
–1

注意事项


输入数据量可以达到30 MB-注意读取速度。

解决方案


解决问题所必须建立的完整网络不过是最小的生成树。 (鉴于在构建开始时已经添加了K条边,因此生成的图形不一定是树,但可以保证包含它。)

在经典设置中,找到最小生成树的问题涉及最小化边缘的总和,但是在这里有必要最小化乘积。 幸运的是,这两棵树重合。 通过用对数替换边缘的权重可以很容易地证明这一点。

构造最小生成树的最著名算法是Prima和Kraskal算法。

在密集图的情况下,Prym算法具有渐近性O(N 2 ),在限制N <10 6的情况下 ,它不允许将其用于问题的完整解决方案。 (但请注意,已对测试数据进行了编译,以使具有所示渐近线的解决方案的得分精确到一半)

使用数组维护有关图连接的组件的当前组成信息的Kruskal算法的幼稚实现具有渐近O(M log M + N 2 )。 在这里,它等于O(N 2 )。 要获得完整的解决方案,您需要使用称为不交集联合(DSU)系统的数据结构。

DSU允许您执行两项操作:find(x)查找x所属集合的“领导者”(我们将领导者转化为集合的编号); 并集(x,y)组合x和y所属的集合。

通常在结构实现中使用的路径压缩启发式算法和等级组合启发式算法允许人们实现渐近行为,在实际情况下,等效于O(1)。

假设我们在执行Kraskal算法的过程中使用DSU来维护有关图连接性组件的信息。 那么该算法的渐近行为将是O(M log M + N)= O(M log M)。

决策代码
 public class CablesSolution { private static final long MODULLO = 2147483647L; public static void main(String[] args) { InputReader in = new InputReader(System.in); PrintWriter pw = new PrintWriter(System.out); int n = in.readInt(); int k = in.readInt(); int m = in.readInt(); DisjointUnionSets dsu = new DisjointUnionSets(n + 1); for (int i = 0; i < k; i++) { int u = in.readInt(); int v = in.readInt(); dsu.union(u, v); } Edge[] edges = new Edge[m]; // (4 + 4 + 8) * 2M = 32M for (int i = 0; i < m; i++) { int u = in.readInt(); int v = in.readInt(); long p = in.readLong(); edges[i] = new Edge(u, v, p); } Arrays.sort(edges); long res = 1; boolean addedEdge = false; for (Edge edge : edges) { if (!dsu.check(edge.getU(), edge.getV())) { dsu.union(edge.getU(), edge.getV()); res = (res * edge.getP()) % MODULLO; addedEdge = true; } } if (!dsu.isJoint()) { res = -1; } else if (!addedEdge) { res = 0; } pw.println(res); pw.flush(); pw.close(); } public static class DisjointUnionSets { private int[] rank; // 4M private int[] parent; // 4M DisjointUnionSets(int size) { rank = new int[size]; parent = new int[size]; for (int i = 0; i < size; i++) { parent[i] = i; } } int find(int target) { if (parent[target] != target) { parent[target] = find(parent[target]); } return parent[target]; } boolean check(int x, int y) { return find(x) == find(y); } void union(int x, int y) { int xRoot = find(x); int yRoot = find(y); if (xRoot == yRoot) { return; } if (rank[xRoot] < rank[yRoot]) { parent[xRoot] = yRoot; } else if (rank[yRoot] < rank[xRoot]) { parent[yRoot] = xRoot; } else { parent[yRoot] = xRoot; rank[xRoot] = rank[xRoot] + 1; } } boolean isJoint() { int parent = -1; for (int i = 1; i < rank.length; i++) { if (parent == -1) { parent = find(i); } else { if (parent != find(i)) { return false; } } } return true; } } private static class Edge implements Comparable<Edge> { private int u; private int v; private long p; Edge(int u, int v, long p) { this.u = u; this.v = v; this.p = p; } int getU() { return u; } int getV() { return v; } long getP() { return p; } @Override public int compareTo(Edge edge) { return Long.compare(p, edge.p); } } } 

B.短语丢失

发言者:德米特里·菲斯科(Dmitry Fisko)

在查看应用程序的指标时,经理Vasily假设界面缺乏吸引用户的热情。 因此,瓦西里(Vasily)要求使设计者Maria的应用程序界面多样化。 经过几幅草图,玛丽终于明白了! 从一开始的想法就浮出水面-事实证明,您需要为文本添加动画。 我们选择了几种文本并制作了动画。 不幸的是,在完成工作之后,动画混合在一起,并且每个动画的源文本都丢失了。 帮助团队了解每种情况下的动画文字。

I / O格式,示例和注释

输入格式


您有几个带有给定动画的文本文件,每个文件都需要选择适当的文本。

链接到带有动画文件的存档

每个动画均由参数定义:
-canvasWidth canvasHeight-动画容器的宽度和高度,在输入的第一行中指定,
-FiguresCount-要动画显示的图形数在输入的第二行中设置,
- 矩形centerX centerY宽度高度角颜色 -声明一个以点(centerX,centerY)为中心的矩形,宽度×高度,旋转角度角度和指定的颜色,
- 圆centerX centerY半径颜色 -声明一个以点为中心的圆(centerX,centerY),半径半径和指定的颜色。

color参数的值可以为{black,red,white,yellow}。 角度参数的取值范围为(-359°,359°)。

对于每个图形,可以一次指定几种类型的动画,这些动画可以并行应用。 此外,每种动画类型最多只能应用于一个图形。 声明图形后立即通过数字0⩽FigureAnimationsCount⩽3设置应用于图形的动画数。

动画类型:
- 移动destX destY时间[周期] -将图形移至以毫秒为单位的点(destX,destY)。
- 旋转角度时间[周期] -图形旋转角度的时间单位为毫秒。
- 缩放destScale时间[周期] -以时间毫秒为单位,将数字增加destScale。

如果指定了cycle参数,则在动画结束时,其运动将沿相反方向继续。

输出格式


播放动画时显示的文本。 对于每个动画文件,答案必须在换行符上指示。 响应中字符的大小写可以是任意的。 动画中找到的每个文本的评分为10分。

例子

进入结论
400 400
10
rectangle 69.000 280.000 24.000 24.000 0.000 black
1
move 251.000 72.000 10000 cycle
rectangle 256.000 188.000 24.000 48.000 0.000 black
0
rectangle 232.000 152.000 72.000 24.000 0.000 black
0
rectangle 35.000 400.000 24.000 24.000 0.000 black
1
move 285.000 -96.000 10000 cycle
rectangle 244.000 248.000 48.000 24.000 0.000 black
0
rectangle 300.000 117.000 24.000 48.000 0.000 black
1
move 112.000 164.000 5000
rectangle 136.000 200.000 72.000 24.000 0.000 black
0
rectangle 160.000 236.000 24.000 48.000 0.000 black
0
rectangle 208.000 224.000 24.000 72.000 44.797 black
1
rotate -44.797 5000
rectangle 232.000 200.000 24.000 24.000 0.000 black
0
42

注意事项


示例中动画的可视化:


解决方案


任务是实现动画。 如果正确实现了动画,则在播放时,会将许多基元添加到可读文本中。 动画可以使用任何方式完成-集成到移动平台或较低级别的解决方案中。

动画示例( 链接到.mov文件 ):



使用来自Python的Tkinter库的示例实现
 import copy import math import time import tkinter from enum import Enum FRAMES_PER_SECOND = 60 TIME_BETWEEN_FRAMES = int(1000 / FRAMES_PER_SECOND) class Rectangle(object): def __init__(self, left_x, left_y, width, height, color, angle=0.0): self.center_x = left_x + width / 2 self.center_y = left_y + height / 2 self.width = width self.height = height self.color = color self.angle = angle def get_left_x(self): return self.center_x - self.width / 2 def set_left_x(self, left_x): self.center_x = left_x + self.width / 2 def get_left_y(self): return self.center_y - self.height / 2 def set_left_y(self, left_y): self.center_y = left_y + self.height / 2 left_x = property(get_left_x, set_left_x) left_y = property(get_left_y, set_left_y) class Circle(object): def __init__(self, left_x, left_y, radius, color): self.center_x = left_x + radius self.center_y = left_y + radius self.radius = radius self.color = color self.angle = 0 def get_left_x(self): return self.center_x - self.radius def set_left_x(self, left_x): self.center_x = left_x + self.radius def get_left_y(self): return self.center_y - self.radius def set_left_y(self, left_y): self.center_y = left_y + self.radius left_x = property(get_left_x, set_left_x) left_y = property(get_left_y, set_left_y) class AnimationType(Enum): MOVE = 1 ROTATE = 2 SCALE = 3 class Animation(object): def __init__(self, time, cycle): self.time = time self.cycle = cycle def scale(self, time_spent): return 1 def move(self, time_spent): return 0, 0 def angle(self, time_spent): return 0 def _cycle_progress(self, time_spent): if self.cycle: cycle_time = time_spent % (self.time * 2) if cycle_time > self.time: return 1 - (cycle_time - self.time) / self.time else: return cycle_time / self.time else: if time_spent < self.time: cycle_time = time_spent % self.time return cycle_time / self.time else: return 1 class MoveAnimation(Animation): def __init__(self, figure, to_x, to_y, time, cycle): super().__init__(time, cycle) self.from_x = figure.center_x self.from_y = figure.center_y self.to_x = to_x self.to_y = to_y def move(self, time_spent): cycle_progress = super()._cycle_progress(time_spent) diff_x = self.to_x - self.from_x diff_y = self.to_y - self.from_y return diff_x * cycle_progress, diff_y * cycle_progress class ScaleAnimation(Animation): def __init__(self, destination_scale, time, cycle): super().__init__(time, cycle) self.destination_scale = destination_scale def scale(self, time_spent): cycle_progress = super()._cycle_progress(time_spent) return 1 + (self.destination_scale - 1) * cycle_progress class RotateAnimation(Animation): def __init__(self, rotate_angle, time, cycle): super().__init__(time, cycle) self.rotate_angle = rotate_angle def angle(self, time_spent): cycle_progress = super()._cycle_progress(time_spent) return self.rotate_angle * cycle_progress class Transformer(object): def scale(self, scale): pass def move(self, diff_x, diff_y): pass def rotate(self, angle): pass class RectangleTransformer(Transformer): def __init__(self, rectangle): self.rectangle = rectangle def scale(self, scale): self.rectangle.width = self.rectangle.width * scale self.rectangle.height = self.rectangle.height * scale def move(self, diff_x, diff_y): self.rectangle.center_x = self.rectangle.center_x + diff_x self.rectangle.center_y = self.rectangle.center_y + diff_y def rotate(self, angle): self.rectangle.angle = (self.rectangle.angle + angle) % 360 class CircleTransformer(Transformer): def __init__(self, circle): self.circle = circle def scale(self, scale): self.circle.radius = self.circle.radius * scale def move(self, diff_x, diff_y): self.circle.center_x = self.circle.center_x + diff_x self.circle.center_y = self.circle.center_y + diff_y def rotate(self, angle): pass class Drawer(object): def draw(self, canvas, spent_time): pass class AnimationApplier(object): @staticmethod def apply_animations(spent_time, transformer, animations): for animation in animations: transformer.scale(animation.scale(spent_time)) diff_x, diff_y = animation.move(spent_time) transformer.move(diff_x, diff_y) transformer.rotate(animation.angle(spent_time)) class RectangleDrawer(Drawer): def __init__(self, rectangle, animations): self.rectangle = rectangle self.animations = animations def draw(self, canvas, spent_time): rect = self._transform_rect_with_animations(spent_time) rect_points = self._get_rectangle_points(rect) rotated_points = self._rotate(rect_points, rect.angle, [rect.center_x, rect.center_y]) canvas.create_polygon(rotated_points, fill=rect.color) def _transform_rect_with_animations(self, spent_time): rect = copy.copy(self.rectangle) transformer = RectangleTransformer(rect) AnimationApplier.apply_animations(spent_time, transformer, self.animations) return transformer.rectangle @staticmethod def _get_rectangle_points(rect): half_width = rect.width / 2 half_height = rect.height / 2 return [ [rect.center_x - half_width, rect.center_y - half_height], [rect.center_x - half_width, rect.center_y + half_height], [rect.center_x + half_width, rect.center_y + half_height], [rect.center_x + half_width, rect.center_y - half_height] ] @staticmethod def _rotate(points, angle, center): angle = math.radians(angle) cos_val = math.cos(angle) sin_val = math.sin(angle) cx, cy = center new_points = [] for x_old, y_old in points: x_old -= cx y_old -= cy x_new = x_old * cos_val - y_old * sin_val y_new = x_old * sin_val + y_old * cos_val new_points.append([x_new + cx, y_new + cy]) return new_points class CircleDrawer(Drawer): def __init__(self, circle, animations): self.circle = circle self.animations = animations def draw(self, canvas, spent_time): circle = self._transform_rect_with_animations(spent_time) size = circle.radius * 2 canvas.create_oval(circle.left_x, circle.left_y, circle.left_x + size, circle.left_y + size, fill=circle.color) def _transform_rect_with_animations(self, spent_time): circle = copy.copy(self.circle) transformer = CircleTransformer(circle) AnimationApplier.apply_animations(spent_time, transformer, self.animations) return transformer.circle class Scene(object): def __init__(self, canvas, animated_symbols): self.canvas = canvas self.drawers = self._get_generated_drawers(animated_symbols) @staticmethod def _get_generated_drawers(animated_symbols): drawers = [] for animated_symbol in animated_symbols: figure = animated_symbol.figure animations = animated_symbol.animations if isinstance(figure, Rectangle): drawer = RectangleDrawer(figure, animations) elif isinstance(figure, Circle): drawer = CircleDrawer(figure, animations) else: raise Exception() drawers.append(drawer) return drawers def draw(self, time_spent): self.canvas.delete("all") for drawer in self.drawers: drawer.draw(self.canvas, time_spent) self.canvas.pack() class Timer(object): def __init__(self): self.started_time = None def start(self): self.started_time = self._current_time() def time_spent(self): current_time = self._current_time() return current_time - self.started_time @staticmethod def _current_time(): return int(round(time.time() * 1000)) def scene_loop(window, scene, timer): time_spent = timer.time_spent() scene.draw(time_spent) window.after(TIME_BETWEEN_FRAMES, scene_loop, window, scene, timer) def configure_window_location(window, canvas_width, canvas_height): screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x = (screen_width / 2) - (canvas_width / 2) y = (screen_height / 2) - (canvas_height / 2) window.geometry('%dx%d+%d+%d' % (canvas_width, canvas_height, x, y)) class AnimatedSymbol(object): def __init__(self, figure, animations): self.figure = figure self.animations = animations class AnimationsParser(object): def parse_animated_symbols(self, file_path): with open(file_path) as f: lines = f.readlines() canvas_width, canvas_height = self._parse_size(lines[0]) symbols_count = int(lines[1]) animated_symbols = [] current = 2 for i in range(symbols_count): figure = self._parse_figure(lines[current]) current += 1 symbol_animations = self._parse_animations(figure, current, lines) current += 1 + len(symbol_animations) animated_symbol = AnimatedSymbol(figure, symbol_animations) animated_symbols.append(animated_symbol) return canvas_width, canvas_height, animated_symbols def _parse_size(self, line): parts = line.split(" ") return int(parts[0]), int(parts[1]) def _parse_animations(self, figure, current, lines): animations = [] animations_count = int(lines[current]) for i in range(animations_count): parts = lines[current + i + 1].split(" ") animation = self._parse_animation(figure, parts) animations.append(animation) return animations def _parse_figure(self, line): parts = line.split(" ") if parts[0] == 'rectangle': center_x = float(parts[1]) center_y = float(parts[2]) width = float(parts[3]) height = float(parts[4]) angle = float(parts[5]) color = parts[6].replace('\n', '') figure = Rectangle(center_x - width / 2, center_y - height / 2, width, height, color, angle) elif parts[0] == 'circle': center_x = float(parts[1]) center_y = float(parts[2]) radius = float(parts[3]) color = parts[4].replace('\n', '') figure = Circle(center_x - radius, center_y - radius, radius, color) else: raise Exception() return figure def _parse_animation(self, figure, parts): if parts[0] == "move": animation = self._parse_move_animation(figure, parts) elif parts[0] == "scale": animation = self._parse_scale_animation(parts) elif parts[0] == "rotate": animation = self._parse_rotate_animation(parts) else: raise Exception() return animation @staticmethod def _parse_move_animation(figure, parts): to_x = float(parts[1]) to_y = float(parts[2]) time = float(parts[3]) cycle = len(parts) >= 5 return MoveAnimation(figure, to_x, to_y, time, cycle) @staticmethod def _parse_scale_animation(parts): destination_scale = float(parts[1]) time = float(parts[2]) cycle = len(parts) >= 4 return ScaleAnimation(destination_scale, time, cycle) @staticmethod def _parse_rotate_animation(parts): rotate_angle = float(parts[1]) time = float(parts[2]) cycle = len(parts) >= 4 return RotateAnimation(rotate_angle, time, cycle) def main(): canvas_width, canvas_height, animated_symbols = \ AnimationsParser().parse_animated_symbols('animation_path.txt') window = tkinter.Tk() configure_window_location(window, canvas_width, canvas_height) canvas = tkinter.Canvas(window, width=canvas_width, height=canvas_height) scene = Scene(canvas, animated_symbols) timer = Timer() timer.start() window.after(TIME_BETWEEN_FRAMES, scene_loop, window, scene, timer) window.mainloop() if __name__ == "__main__": main() 

C.俄罗斯方块机器人

作者:德米特里·涅夫多姆斯基
时间限制1秒
内存限制256兆字节
进入标准输入或input.txt
结论标准输出或output.txt
忙碌的Arkady喜欢花空闲时间参加各种复古计算机拍卖。 在其中之一上,他看到了见过生活的Commodoro,Arkady急忙购买了。 这次他不仅购买了硬件,而且还购买了软件,计算机上等待着他的是一个惊喜-前任老板还没有完成俄罗斯方块。 根据经验,阿卡迪(Arkady)阐明了自己的规则。

为玩家提供了10个单元格宽度和无限高度的俄罗斯方块场。 必须在字段上放置的所有四氨基的列表。 四聚胺立即“下落”,即它们的位置为相应的移动设置一次,并且在下落期间,位置和旋转无法更改。

Tetramino摔倒直到跌倒在底部或其他物体上。 如果同时填满该字段的整个行,则该行会消失,并且其上方的所有内容都将落在下方的一个单元格中。

获得的点数由破坏的行数确定。 放置所有建议的棋子后,游戏结束。 游戏的作者留下一张记录表,他本人占据了所有位置。

如前所述,Arkady是一个非常忙碌的人。 但是他也很赌博。 帮助他成为这个游戏中最好的。

图表及其可能的转弯

I / O格式,示例和注释

输入格式


输入数据位于文件input1.txt,input2.txt,...,input10.txt中。 存档文件

第一行包含正数N(N≤100)。

在随后的每一行中,将传输来自集合{'O', 'S', 'Z', 'L', 'J', 'T', 'I'}的单个拉丁字符。

每个符号代表一个四氨基图。

输出格式


为了进行验证,您必须提交一个zip归档文件,其输出文件位于其根目录中,名称为output1.txt,output2.txt,...,output10.txt,其中输出文件outputX.txt应对应于输入文件inputX.txt。
响应文件由N行组成,每行对应于输入数据中的一个四米诺骨牌,并且由两个数字A i和B i组成 ,指示玩家按照建议的图形移动。

1. A i表示最左边的四氨基元素的列(1≤A i≤10)。
2. B i表示四氨基(1≤B i≤4)的旋转方向。

如果所需文件不在存档中或格式不正确,则对应的测试将获得0分。

例子

进入结论
15
Z
O
L
O
J
S
T
J
Z
J
Z
S
Z
I
O
1 2
9 1
6 4
9 1
3 2
6 1
1 2
4 1
6 2
3 1
8 2
2 2
4 1
10 1
6 1

注意事项


在此示例中,将销毁4行。

字段的最终状态:



错误代码信息:

1. PE-发生输出错误(例如,该图没有旋转)
2. WA-正确提供了有关图形旋转和位置的信息,但是这种布置超出了领域的范围。
3. TL,ML,OK,RE-这些代码与Yandex.Contest系统规则中的含义相同。

由于问题是NP完全问题,因此从数学角度来看,解决方案不一定是最优的。 决策的有效性取决于得分的分数。

解决方案


这是一项开放测试任务。 让我们弄清楚这意味着什么。

参与者可以随意使用一个zip存档,其根目录为文本文件input1.txt,input2.txt,...,input10.txt。 一个文件是一组要排列的四氨基图形。 作为参与者的回应,应该会生成一个zip归档文件,其根目录为文件output1.txt,output2.txt,...,output10.txt。 每个文件都必须包含在相应集合的文件中指定的顺序相同的移动顺序。

该任务是NP完成的,而100%的解决方案只是穷举搜索。 因此,复杂度随着图形数量的增加而呈指数增长。 由于不必交出解决方案代码,因此在内存和执行时间上没有任何限制。

当达到“令人满意”的质量时,该任务的满分为-解决方案应获得60%的分数。 作者对问题的解决方案的得分也一样高,这是基于在标准Tetris随机发生器上训练的遗传算法得出的。 该文章介绍此随机化器。

剩下的任务与编写俄罗斯方块的常规bot并没有太大区别,只是比赛场地的“玻璃杯”高度不受限制。 因此,决定成功与否取决于机器人在不超出“眼镜”范围的前提下可以玩多长时间,而是取决于机器人能否成功排列有限的一组数字。

参与者可以自由选择解决方案策略。 以下是其中一些顺序,以增加复杂性和增加分数的顺序:

1.我们不使用代码就玩“手牌”,写下解决方案中的动作。
2.我们编写一个可视化工具并玩“手”,写下解决方案中的动作。
3.贪婪算法。 在每个回合中,我们都会尝试放置一块,以使破坏的行数达到最大。 如果有多个这样的选项,请随机选择其中任何一个。
4.与第3段相同的贪婪算法,只是检查的不是设置一个图形的成功,而是穷举搜索一组图形(例如,一组五个图形)的成功。
5.遗传算法。 我们在适应度函数中不仅考虑了贪婪算法中的断行数,还考虑了字段“浮雕”的平滑度,例如,图形之间形成的自由空间的数量。

D和E.Quest

作者:Ilimdar Ablyaev和Ilya Kuteev

Misha在任务类型中制作了一款手机游戏。 他在互联网上找到了一个带有游戏引擎的现成库,并基于该库创建了一个界面。 Misha急于发布游戏,并在开发过程中犯了一些错误。 为了按时完成项目,Misha向您寻求帮助。 下载项目,更正编译错误,崩溃和其中的逻辑错误。 完成任务,然后转到Bingo屏幕,该屏幕上会出现一个秘密短语。 她将是这个问题的答案。

使用以下项目下载档案: AndroidiOS 。 输出格式是一个秘密短语(字符串,最多255个字符)。

Android解决方案


1) .

2) , IDE.

  1. final mBackButton mTitleTextView, .
  2. mTitleTextView mBackButton , , .
  3. — Layout ViewGroup.
  4. okButton getTextTaskControlsLayout.
  5. IDE , «» «» .

3) . ds , «java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity». .

4) .

  • — java.lang.IndexOutOfBoundsException.
  • : Layout Layout. .
  • 1.1 ( ).

5) .

  • , .
  • ( , , ).
  • . :
    — Layout.
    — .
  • .

6) . — .

7) «».

  • android.content.res.Resources$NotFoundException.
  • . , ID .
  • .

! .

iOS


. :



, Optional<Optional<Step>>. guard let Optional . Optional, flatMap({ $0 }), .



, .



, QuestCore . :



, — goLeft, goRight . , goRight .



, : TestView, firstResponder . , TestView firstResponder. , UIKit.



«». IBOutlet, , . .







, 2018 .

Source: https://habr.com/ru/post/zh-CN482210/


All Articles