How to Recreate the Viral ‘Sheep a Sheep’ Puzzle Game in Godot
This article walks through recreating the popular ‘Sheep a Sheep’ puzzle game using Godot Engine, detailing the game’s mechanics, three stack patterns, data structures, detection of selectable tiles, level generation, shuffling, mask handling, and inter‑object communication, while sharing code snippets and design insights.
Introduction
Yesterday a friend asked me to clone the viral game "Sheep a Sheep" because it is notoriously hard. I have not played the original, but I will attempt a remake using Godot Engine. The project is open‑source on GitCode.
Gameplay Overview
The game resembles classic tile‑matching titles such as LianLianKan and is often compared to 3tiles . It is a low‑complexity casual puzzle, but the second level has an extremely low pass rate, which sparked widespread discussion.
Implementation Overview
The overall implementation is simple, yet three key aspects require attention:
Design of the stack (card pile) data structure.
Detection and updating of selectable ("window") cards.
A "window card" is a card that can currently be picked from a stack.
Stack Structure and Data Model
The stack consists of three pattern types (A, B, C) that can be combined to form any level layout. Each pattern is essentially a three‑dimensional array.
Pattern A (blue circle): a card covers exactly one card below it; when the upper card is removed, the lower card becomes a window card.
Pattern C (red circle): a card can cover up to four cards below it; a card becomes a window card only when all four covering cards are removed.
Pattern B (implied): a card covers two cards below it; similar logic applies.
All three patterns can be represented as a 3‑D array where the x, y, and z dimensions correspond to width, height, and depth.
Below are visual illustrations of the three patterns:
Base Container Class (GDScript)
#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 basic stack is a three‑dimensional array (x * y * z) that can be shaped into columns, rows, or pyramids without affecting later logic.
Mask Definition
A mask array defines which positions contain tiles, allowing custom shapes such as the "S" pattern used for the CSDN logo.
# 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]
]Detecting Window Cards
Each stack pattern derives from MContainerBase and implements its own detection logic.
Pattern A – Single Cover
#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)Pattern B – Double Cover
#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)Pattern C – Quad Cover
#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)Level Generation
Each level must contain a multiple of three of every tile type. The following script builds a pool of tiles, shuffles it, and uses it to populate the board.
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 dictionary keys represent tile patterns; the values indicate how many groups of three appear in the level. After populating the pool, the array is shuffled to randomize tile placement.
Shuffling Tool
func shuffle_tiles():
tiles.shuffle()
tiles_index = -1
func redistribute_face() -> int:
tiles_index += 1
return tiles[tiles_index]When the player triggers a shuffle, the current board tiles are recorded, the array is shuffled, and each board position receives a new tile value.
Mask File Reading
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 dataGodot’s str2var conveniently converts a string representation of a mask into a usable array.
Object Communication
The game relies heavily on Godot’s group system for communication between tiles, stacks, and levels. This mechanism allows any node to broadcast or listen for events without tight coupling.
Conclusion
Recreating the "Sheep a Sheep" puzzle in Godot is technically straightforward; however, the game’s intentional design quirks—such as hidden dead‑ends and strict tile‑count constraints—turned its flaws into a viral hook, demonstrating how gameplay mechanics can become a marketing advantage.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
