laitimes

The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

author:Game Grape
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

Editor's note: Some time ago, the ultra-casual match-3 game "Sheep a Sheep" exploded, and many netizens said that the second level of the game was too difficult, and even speculated that "there is no solution to the level at all". And the author of this article, @ Lao Wang of the development game, after trying to reproduce the game, put forward a conjecture: the excessive difficulty may stem from flaws at the code level.

The following is the main text:

Yesterday, a friend said to me: "Recently, a game called "Sheep a Sheep" exploded, it is too difficult to play, can you copy a no? ”

It is said that the last time I played a casual game was a few years ago, but the trust of friends must go to the soup to fight the fire, ah, without saying a word, open the whole! However, the impulse is the devil, until this moment, Lao Wang has not been able to play an original game by hand, I don't know whether the game entrance is designed to be too hidden or the network loading is too slow, whether it is a mobile phone or a PC, the game stays in the following interface.

The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

Therefore, the fork of this game is completely based on the results of cloud observation of various video websites, fortunately, the gameplay of the game is not particularly difficult to understand. The development tool used for forking is Godot Engine (the development principle is similar with other tools), and the project has been open sourced to GitCode:

Godot version of "Sheep a Sheep":

https://gitcode.net/hello_tute/SheepASheep

Next, I will speculate about the implementation principle of this mini-game by copying the game, this article is mainly for friends who are interested in game development, welcome to give more valuable advice.

01 Gameplay

The first time he saw "Sheep a Sheep", Lao Wang first thought of the "Lian Lian Look" of that year, but some netizens broke the news that the game "borrowed" from "3tiles". I glanced at "3tiles" and was relatively similar. To be honest, there is nothing too remarkable about the gameplay of this game, and it is a decent "low calorie" casual game.

The reason why it has become a topic work is mainly because its extremely low pass rate in the second level has aroused the desire of many players to challenge.

And to this day, this "low pass rate" has also been revealed by many players on the network, and the second level is actually a dead end in itself in large probability. Did the programmer deliberately dig a pit and set up a dead end? Let's talk about the development of the game first, and then you'll have the answer yourself.

02 Implementation Overview

The game as a whole is simple, but there are a few implementations to keep an eye on:

  • Implementation of the stack data structure
  • How to detect and update pickable cards
To make a small definition, the cards that can be picked up in a deck will be referred to as "window cards".

01 Structure of the deck

The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

At first, I was really blinded by this complex deck structure, but a closer look reveals that no matter how complex the deck is, it's actually a patchwork of the following three deck patterns:

  • The blue circle circles the card pile pattern A: the upper 1 card only blocks the lower 1 card; At the same time, the lower card is only blocked by the upper 1 card. As long as the top 1 card is removed, the lower card becomes a window card;
  • The red circle circles the card pile mode C: 1 card above can block the next 4 cards; At the same time, the lower card may be blocked by the 4 cards above, and only if the 4 cards above it are taken away will it become a window card itself.

Although the above figure is not very obvious, it is not difficult to guess the existence of the third deck mode B, that is:

  • 1 card above can block the bottom 2 cards; At the same time, the lower card may be blocked by the 2 cards above, and only if the 2 cards above it are taken away will it become a window card by itself.

For deck mode A, some friends will be eager to implement it with "queue" or "stack", which has two disadvantages:

  • Logically, the window card of deck mode A may also be 2-dimensional, and if it is implemented with queues, it limits its flexibility;
  • Both card pile patterns B and C are not easy to achieve with queues, so if you want to pursue the unity of the data structure, you have to find another way.

In fact, whether it is the card pile mode A, B or C, it is just a 3-dimensional array structure, and the pattern A in the above figure looks special, nothing more than its x and y dimensions are 1. The difference between the three types of decks is nothing more than a way to check if a new window card appears on the deck when a window card is removed.

  • Deck Mode A
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced
  • Deck Mode B
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced
  • Deck mode C
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

The data structure of the 02 pile

I define it as the MContainerBase base class

#MContainerBase
extends Node2D
class_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)           

The most basic deck is a three-dimensional array of x*y*z, and we can use any method to construct the queuing shape we want: bar, bar, or even pyramid. This will not affect the implementation of the later programs.

In order to increase the diversity of this "large square" in the project, I also set up the following "mask" for it, which is the origin of the CSDN text in the game. Of course, we can also freely define the window card through the "mask", and this part is free for everyone to play.

# 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],
]           
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

03 How to detect and update pickable cards

The three deck patterns are derived from MContainerBase and correspond to the following three detection methods:

  • Stack Mode A: Detect only if there are cards directly above you
#1 Cover 1
extends 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)           
  • Stack Mode B: Detects whether there are cards in the two directions above you
#1 Cover 2
extends 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)
           
  • Stack Mode C: Detects whether there are cards in the quad above you
#1 Cover 4
extends 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)
           

In Godot, these three deck modes can also be crafted into prefabricated bodies through scene nodes, so that level designers can easily create beautiful levels.

The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

03 How to build a new level

After a brief understanding of the rules of the game, it is not difficult to deduce that one of the necessary conditions for each level to be passed is that the total number of each pattern must be divisible by 3. Here's how to do this:

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           

The key of the dictionary initial_tiles corresponds to each pattern, and the value after it corresponds to the "logarithm" of the pattern that appears in this level (here 1 pair is equal to 3). Multiply the value by 3 into the array tiles (hereinafter referred to as: the pool of pending cards), and then disrupt the elements in the pool of pending cards and wait for the "deal".

01 About the pit in the game

Many friends complained: "Programmers deliberately dig pits to make dead levels". In fact, he does not have to deliberately dig the pit, because the game itself has a lot of "natural pits", if you do not fill the pit hard, they naturally belong to you. And here are a few deadly pits: at first glance, all the patterns in the pool can be divided by 3, so it must be cleared? That's not necessarily true:

  • Only when the number of cards in the tabletop deck is the same as the number of cards in the pool to be dealt can all the cards "land", and how many (layers) there are in the tabletop pile in the game is itself a mystery. And if I'm not mistaken, in each round the designer first needs to make sure that the stack is in good shape, and then make sure that the number of cards in the pile matches the number of cards in the pool to be played. Even if the difference between the two is 1, it will cause a dead end.
  • As mentioned above, the number of table cards and the number of cards in the pool to be dealt are only necessary and not sufficient to pass the level. Even if this condition is met, if the number of cards on the window is too small relative to the number of cards on the table and the number of patterns, it will cause a deadbeat. For example, this extreme example is this: Suppose the game has a total of 15 suits, and there is only this mode A pile on the table, which has 90 cards. Then as long as the player does not encounter 3 cards of the same pattern when picking up cards for 7 consecutive times, he will "definitely die".
The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

In fact, this game, on the one hand, to control the difficulty of the level, on the other hand, to ensure that the level can be cleared itself is a rather difficult problem (at least Lao Wang did not come up with a way).

And the designer did the opposite, (probably) did not spend effort to design the algorithm, leaving the pit to the player, got a very low pass rate, but created a topic and formed a hit.

In this way, this is indeed a clever "design". But Lao Wang believes that this kind of "design" is not suitable for reference in game planning, just like the suspense dramas that are now flooding the market, starting to bury countless pits, satisfying the audience's appetite, and finally failing to end it, and in the long run, the audience's (player's) trust in suspense dramas (games) has been consumed.

02 The realization of shuffle props

The implementation principle of shuffling is very simple, record the current desktop cards in an array tiles, when it is necessary to shuffle the cards, first disrupt the order of the cards in the array, and then let each card on the desktop take a new value in the tiles. Another dazzling animation, it's really quite like that.

The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced
funcshuffle_tiles:
		
tiles.shuffle
tiles_index = -1
funcredistribute_face-> int:
tiles_index +=1
returntiles[tiles_index]
            

03 Mask file read

Here to boast about Godot Engine, many of its features are really convenient, such as the following str2var can simply and rudely convert strings directly to object types.

class_nameFileReader
staticfuncread(path,default_data):
 vardata = default_data
 varfile =File.new
 file.open(path,File.READ)
 varcontent :String= file.get_as_text
 ifnot content.empty:
 data = str2var(content)
 file.close
 returndata           

04 Communication between objects

There is a lot of communication between objects in this mini-game: between cards and cards, between cards and stacks, between cards and levels, between stacks of cards and levels. In order to implement the game quickly, I made a lot of use of the Group mechanics of the Godot Engine, and I have to say that the Group is one of the best designs of the Godot Engine.

The programmer spent 12 hours replicating "Sheep a Sheep", and the code has been open sourced

04 Summary

The mini-game "Sheep a Sheep" is not difficult from the perspective of planning and development, but the "flaw" can become a "gimmick", and people have to sigh "The game world is really possible".

About author:Lao Wang, who develops games, is a college teacher, technical columnist, and independent game developer.