当前位置:网站首页>Make a game by yourself with pyGame 01
Make a game by yourself with pyGame 01
2022-07-27 08:32:00 【Sizchristov】
Catalog
What functions need to be realized
How to realize basic functions
introduction
Studying recently java Outside of , In my spare time, I wrote a novel based on pygame The game of , Mainly want to test pygame All aspects of performance , The second is because python The simplicity of language , It's really comfortable to write , Finally, it's fun to entertain yourself ( Focus on ). Statement : All the codes of the game are original , Made by hand . I will put the complete file package required for the operation of the game at the end of the text , If you need any friend, please take it by yourself .
What kind of game to create
because pygame It is a lightweight game development tool , The functions that can be realized are also relatively limited , So for simplicity , Convenient for operation and other purposes , This article achieves various purposes by creating a basic rectangular square , Because the rectangular box is the main operation object , So the game is named 《The Box》.
What functions need to be realized
As a test development game , The functions to be realized are relatively basic , Because it is a two-dimensional graphic game , Therefore, we need to realize some traditional functions :
- Creation and interaction of main operation objects of the game
- Basic keyboard and mouse interaction in the game
- The construction of the scene in the game and the interaction with other objects
- Creation and interaction of other non environment objects in the game
- The realization of basic loop logic in the game
In addition to the above basic functions that need to be realized , We also need to consider the basic logic construction of the game , The so-called logical construction is to achieve the purpose of the game , For example, whether the game is interesting enough , Whether it is playable , Whether there is enough interactivity , Of course, these are not within the scope of this article ( sad ).
How to realize basic functions
At present, it has only preliminarily realized some basic functions , Because I'm busy recently, I suspended development , So first post it to see if you can pick it up one day ( laugh ). I don't say much nonsense , Let's first look at the current implementation effect :
《 The Box》
It looks ok , It's just too stupid ( laugh ). Well, about how to achieve the problem , In fact, we only need to start with modular design . This game is mainly realized through several basic modules : Background module 、 Main role module 、 Scene module 、 Logic modules . The main functions and codes of the above modules are as follows :
Background module
class BackgroundWall(py.sprite.Sprite):
def __init__(self, screen):
py.sprite.Sprite.__init__(self)
wallPath = '../ Material library / Background wall material /'
self.index = 0
self.imageFiles = [os.path.join(wallPath, i) for i in os.listdir(wallPath)]
self.images = [py.transform.smoothscale(py.image.load(name), (screen.get_width(), screen.get_height())) for name
in self.imageFiles]
self.image = self.images[self.index]
self.rect = self.image.get_rect()
self.rect.topleft = (0, 0)
def update(self, game_time):
timeH, timeM = divmod(game_time, 60)
self.index = int(timeH)
if self.index>17:
self.index =17- self.index%17
self.image = self.images[self.index]
self.rect = self.image.get_rect()
self.rect.topleft = (0, 0)As the name suggests, the background module is used to create the background of the game , The function of this article is also extremely simple , It mainly realizes the automatic switching of the game background by calling the game time . If you want to realize various functions , For example, with BGM Switch background , Or you can switch the background through some event , Only need update Add various required functions to the function .
Main role module
class CubeHuman(object):
def __init__(self, screen):
self.x = screen.get_width()
self.y = screen.get_height()
self.CubeBodyGroup = py.sprite.Group()
self.CubeEyeballGroup = py.sprite.Group()
self.CubeMouthGroup = py.sprite.Group()
self.CubeEyesGroup = py.sprite.Group()
self.cube_body = self.CubeBody()
self.cube_eyes = self.CubeEyes()
self.cube_eyeball = self.CubeEyeball()
self.cube_mouth = self.CubeMouth()
self.CubeBodyGroup.add(self.cube_body)
self.CubeEyeballGroup.add(self.cube_eyeball)
self.CubeMouthGroup.add(self.cube_mouth)
self.CubeEyesGroup.add(self.cube_eyes)
# damage , Blood volume and other self parameters
self.basicDamage = 10 # Basic damage
self.basic = 10 # Basic defense
self.basicHp = 100 # Basic blood volume
self.basicMp = 100 # Basic blue quantity
self.HP = 100
self.MP = 100
self.rate = 4
hpImage = py.image.load('../ Material library / Blood and blue bars / Blood strip .png')
hpRect = hpImage.get_rect()
self.hpImage = py.transform.smoothscale(hpImage,
(int(hpRect.width / self.rate), int(hpRect.height / self.rate)))
self.hpRect = self.hpImage.get_rect()
mpImage = py.image.load('../ Material library / Blood and blue bars / Blue stripe .png')
self.mpImage = py.transform.smoothscale(mpImage, (self.hpRect.width, self.hpRect.height))
self.mpRect = self.mpImage.get_rect()
hpWordImage = py.image.load('../ Material library / Blood and blue bars /HP.png')
hpWordRect = hpWordImage.get_rect()
self.hpWordImage = py.transform.smoothscale(hpWordImage,
(int(hpWordRect.width / self.rate),
int(hpWordRect.height / self.rate)))
self.hpWordRect = self.hpWordImage.get_rect()
mpWordImage = py.image.load('../ Material library / Blood and blue bars /MP.png')
self.mpWordImage = py.transform.smoothscale(mpWordImage, (self.hpWordRect.width, self.hpWordRect.height))
self.mpWordRect = self.mpWordImage.get_rect()
self.lossImage = py.image.load('../ Material library / Blood and blue bars / Blank bar .png')
self.hpWordRect.topleft = (5, 5)
self.mpWordRect.topleft = (5, self.hpWordRect.height + 10)
self.hpRect.topleft = (self.hpWordRect.right, self.hpWordRect.top)
self.mpRect.topleft = (self.mpWordRect.right, self.mpWordRect.top)
def DrawAttributes(self, screen):
rateHp = 1 - (self.HP / self.basicHp)
rateMp = 1 - (self.MP / self.basicMp)
lossHPImage = py.transform.smoothscale(self.lossImage, (int(self.hpRect.width * rateHp), self.hpRect.height))
lossHPRect = lossHPImage.get_rect()
lossHPRect.topright = self.hpRect.topright
lossMPImage = py.transform.smoothscale(self.lossImage, (int(self.mpRect.width * rateMp), self.mpRect.height))
lossMPRect = lossMPImage.get_rect()
lossMPRect.topright = self.mpRect.topright
screen.blit(self.hpWordImage, self.hpWordRect)
screen.blit(self.mpWordImage, self.mpWordRect)
screen.blit(self.hpImage, self.hpRect)
screen.blit(self.mpImage, self.mpRect)
screen.blit(lossHPImage, lossHPRect)
screen.blit(lossMPImage, lossMPRect)
class CubeBody(py.sprite.Sprite):
def __init__(self):
rate = 3 # Zoom ratio , The same below
py.sprite.Sprite.__init__(self)
self.speed = 1
self.image = py.image.load('../ Material library / Character material / Square man /body/body_basic.png').convert_alpha()
self.rect = self.image.get_rect()
self.image = py.transform.smoothscale(self.image,
(int(self.rect.width / rate), int(self.rect.height / rate)))
self.rect = self.image.get_rect()
self.rect.topleft = (0, 0) # Initial build location
self.gravity = 2 # The coefficient of gravity is 2, Indicates that the image of each adjacent frame drops 2 Pixel
self.startJumpPos = 0
self.on_ground = False
self.jump = False
self.jumpMaxDis = 100 # Maximum jump height ( Pixels )
self.lastPos = None
self.nowPos = None
def KeyControl(self, key):
if key[py.K_d]:
if key[py.K_LSHIFT]:
self.rect.x += self.speed + 1
else:
self.rect.x += self.speed
if key[py.K_a]:
if key[py.K_LSHIFT]:
self.rect.x -= self.speed + 1
else:
self.rect.x -= self.speed
if key[py.K_SPACE] and self.on_ground:
self.gravity = -4 # Jump speed
self.on_ground = False
self.jump = True
self.startJumpPos = self.rect.y
self.lastPos = self.nowPos
def update(self, floor, screen):
if len(self.rect.collidelistall(floor.floorBody)) > 1: # Collision detection with wall
self.rect.x = self.lastPos
if self.rect.collidelist(floor.floorTop) == -1: # Collision detection with the ground
self.on_ground = False
if self.rect.right > screen.get_width() or self.rect.left < 0: # Prevent movement beyond the edge of the screen
self.rect.x = self.lastPos
if not self.on_ground and not self.jump:
self.rect.y += self.gravity
if self.rect.collidelist(floor.floorTop) != -1:
self.on_ground = True
if not self.on_ground and self.jump and (self.startJumpPos - self.rect.y) <= self.jumpMaxDis:
self.rect.y += self.gravity
else:
self.jump = False
self.gravity = 2
self.startJumpPos = 0
self.nowPos = self.rect.x
class CubeEyes(py.sprite.Sprite):
def __init__(self):
rate = 2
py.sprite.Sprite.__init__(self)
self.image = py.image.load('../ Material library / Character material / Square man /eyes/eyes_black.png').convert_alpha()
self.rect = self.image.get_rect()
self.image = py.transform.smoothscale(self.image,
(int(self.rect.width / rate), int(self.rect.width / rate)))
self.rect = self.image.get_rect()
def EyesMove(self, pos, mouse_pos, eyeball):
x0, y0 = pos[0], pos[1]
x1, y1 = mouse_pos[0], mouse_pos[1]
R = eyeball.rect.width / 2
r = self.rect.width / 2
sita_Angel = np.sign(y1 - y0) * np.pi / 2
if x1 != x0:
sita_Angel = np.arctan((y1 - y0) / (x1 - x0))
x2, y2 = x0 + np.sign(x1 - x0) * R * np.cos(sita_Angel), y0 + np.sign(x1 - x0) * R * np.sin(sita_Angel)
x3, y3 = x2 - np.sign(x1 - x0) * r * np.cos(sita_Angel), y2 - np.sign(x1 - x0) * r * np.sin(sita_Angel)
return x3, y3
def update(self, eyeball, body, screen):
pos = eyeball.rect.center
self.rect.center = pos
rect = self.rect.copy()
rect.center = (rect.center[0] + int(body.rect.width / 2), rect.center[1])
''' The mouse controls the direction of the eyes '''
mousePos = py.mouse.get_pos()
x1, y1 = self.EyesMove(self.rect.center, mousePos, eyeball)
x2, y2 = self.EyesMove(rect.center, mousePos, eyeball)
self.rect.center = (int(x1), int(y1))
rect.center = (int(x2), int(y2))
screen.blit(self.image, rect)
class CubeEyeball(py.sprite.Sprite):
def __init__(self):
rate = 2
py.sprite.Sprite.__init__(self)
self.image = py.image.load('../ Material library / Character material / Square man /eyeball/eyeball_basic.png').convert_alpha()
self.rect = self.image.get_rect()
self.image = py.transform.smoothscale(self.image,
(int(self.rect.width / rate), int(self.rect.width / rate)))
self.rect = self.image.get_rect()
def update(self, body, screen):
pos = body.rect.center
self.rect.center = (pos[0] - int(body.rect.width / 4), pos[1] - int(body.rect.height / 4))
rect_right = self.rect.copy()
rect_right.center = (rect_right.center[0] + int(body.rect.width / 2), rect_right.center[1])
screen.blit(self.image, rect_right)
class CubeMouth(py.sprite.Sprite):
def __init__(self):
rate = 3
py.sprite.Sprite.__init__(self)
self.image = py.image.load('../ Material library / Character material / Square man /mouth/mouth_basic.png').convert_alpha()
self.rect = self.image.get_rect()
self.image = py.transform.smoothscale(self.image,
(int(self.rect.width / rate), int(self.rect.height / rate)))
self.rect = self.image.get_rect()
def update(self, body):
pos = body.rect.center
self.rect.center = (pos[0], pos[1] + int(body.rect.height / 4))
def update(self, floor, screen):
self.DrawAttributes(screen)
self.CubeBodyGroup.update(floor, screen)
self.CubeBodyGroup.draw(screen)
self.CubeEyeballGroup.update(self.cube_body, screen)
self.CubeEyeballGroup.draw(screen)
self.CubeMouthGroup.update(self.cube_body)
self.CubeMouthGroup.draw(screen)
self.CubeEyesGroup.update(self.cube_eyeball, self.cube_body, screen)
self.CubeEyesGroup.draw(screen)This code looks very verbose , It is mainly because several inner classes are created to realize the main role about eyes , The body , Control of mouth and other parts , By modifying each component , It can realize extremely complex functions , Here is just a simple realization of the mouse control function of the eyes , That is, the eyes follow the mouse , Even your mouth can follow the mouse if you want ( laugh ). The second is to control the forward and backward movement of objects through the keyboard , For example, press and hold shift It can speed up the movement of objects , Press the space to realize the jumping of objects . Finally, about the interaction between objects and scenes , For example, judge whether the object is on the ground , Whether it has reached the game boundary . There is also the generation of some attributes of roles , Such as blood strips .
Scene module
The scene module is divided into the following small modules :
Floor module :
class FloorGen(object):
def __init__(self, screen, cube):
self.x = screen.get_width()
self.y = screen.get_height()
self.floorPos = []
self.floorImage = []
self.floorRect = []
self.floorTop = []
self.floorBody = []
self.jumpMaxDis = cube.cube_body.jumpMaxDis
self.roadLength = random.randint(20, 25) # The total length of the game interface
''' Parameter setting of the floor itself
Respectively represents the unit floor width , Height '''
self.width = int(self.x / 4)
self.heightA = int(self.y / 25)
self.heightB = int(self.heightA / 4)
''' Floor generation follows certain rules , The maximum distance difference between adjacent floors does not exceed the maximum jumping limit of the block '''
def floor_pos_gen(self):
self.floorPos = [[(0, random.randint(int(self.y * (1 - 1 / 3)), self.y - self.heightB))]]
for group in range(1, self.roadLength):
body = [(group * self.width,
random.randint(max(self.floorPos[group - 1][0][1] - self.jumpMaxDis, self.y / 2),
min(self.floorPos[group - 1][0][1] + self.jumpMaxDis, self.y - 5)))]
self.floorPos.append(body)
for floor in self.floorPos:
num = int(np.ceil(self.y - floor[0][1] - self.heightB) / self.heightA) + 1
head = floor[0][1] + self.heightB
floor.append((floor[0][0], head))
for member in range(1, num):
nextHead = head + self.heightA * member
floor.append((floor[0][0], nextHead))
def floor_Image(self):
floorImage = []
for i in range(5):
locals()['image{}'.format(i)] = py.image.load('../ Material library / Floor material /floor_lv{}.png'.format(i)).convert_alpha()
floorImage.append(eval('image{}'.format(i)))
floorImage[i] = py.transform.smoothscale(floorImage[i], (self.width, self.heightA))
floorImage[0] = py.transform.smoothscale(floorImage[0], (self.width, self.heightB))
self.floorImage = floorImage
def generator(self, screen):
self.floor_pos_gen()
self.floor_Image()
for floor in self.floorPos:
rectLs = []
for i in range(len(floor)):
floorPos = floor[i]
image = [self.floorImage[-1] if i > len(self.floorImage) - 1 else self.floorImage[i]][0].copy()
rect = image.get_rect()
rect.topleft = floorPos
screen.blit(image, rect)
rectLs.append(rect)
self.floorBody.append(rect)
self.floorRect.append(rectLs)
self.floorTop.append(rectLs[0])
# Update all floor images
def update(self, screen):
for floorL in self.floorRect:
for i in range(len(floorL)):
floorRect = floorL[i]
floorImage = [self.floorImage[-1] if i > len(self.floorImage) - 1 else self.floorImage[i]][0].copy()
screen.blit(floorImage, floorRect)
def updatePre(self, cube, point):
for floorL in self.floorRect:
for floorRect in floorL:
floorRect.x -= cube.cube_body.nowPos - cube.cube_body.lastPos
cube.cube_body.rect.x = cube.cube_body.lastPosThe floor module is used to create the ground . In this paper, a certain length of ground is randomly generated to realize the movement of the game scene , At the same time, the height between adjacent floors cannot exceed the maximum jumping height of the main character , This is to avoid that the box cannot cross the game scene .
Cloud module :
class Cloud(py.sprite.Sprite):
def __init__(self, screen, game_time):
py.sprite.Sprite.__init__(self)
cloudPath = '../ Material library / cloud /'
rate = 3
self.speed = 1 # Movement speed
self.genTime = game_time
self.imageFiles = [os.path.join(cloudPath, i) for i in os.listdir(cloudPath)]
self.images = [py.image.load(name) for name in self.imageFiles]
self.rects = [image.get_rect() for image in self.images]
self.images = [
py.transform.smoothscale(self.images[i],
(int(self.rects[i].width / rate), int(self.rects[i].height / rate)))
for i in range(len(self.images))]
self.index = random.randint(0, len(self.images) - 1)
self.image = self.images[self.index]
self.rect = self.image.get_rect()
self.width = screen.get_width()
self.height = screen.get_height()
self.x = random.randint(int(self.width + self.rect.width / 2), int(self.width + self.rect.width / 2 + 100))
self.y = random.randint(int(self.rect.height / 2), int(self.height / 4))
self.rect.center = (self.x, self.y)
def update(self):
self.rect.x -= self.speed
if self.rect.right < 0:
self.kill()
# Control the cloud generation rate
def cloudGen(cloud_group, screen, game_time, ls):
if len(cloud_group) == 0:
cloud = Cloud(screen, game_time)
cloud_group.add(cloud)
elif 0 < len(cloud_group) < 3:
spriteTime = list(cloud_group.spritedict.keys())[-1].genTime
if len(ls) == 0:
timeSlot = random.randint(3, 50)
ls.append(timeSlot)
else:
timeSlot = ls[-1]
timeWait = (game_time - spriteTime)
if timeWait >= timeSlot:
ls.pop(-1)
cloud = Cloud(screen, game_time)
cloud_group.add(cloud)
This module is used to generate some clouds , At the same time, enter and leave the game interface at a certain speed , This makes the scene look more dynamic .( Don't say the texture is too ugly , It's all in wps Designed one by one )
The sun and the moon :
# The sun
class SunAndMoon(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.rate = 3
self.MoonFilePath = '../ Material library / The moon /'
self.imageRaw = py.image.load('../ Material library / The sun /sun.png').convert_alpha()
self.MoonFiles = [os.path.join(self.MoonFilePath, i) for i in os.listdir(self.MoonFilePath)]
self.imageMoonRaw = [py.image.load(name).convert_alpha() for name in self.MoonFiles]
self.MoonRect = [image.get_rect() for image in self.imageMoonRaw]
self.imageMoon = [py.transform.smoothscale(self.imageMoonRaw[i], (int(self.MoonRect[i].width / self.rate),
int(self.MoonRect[i].width / self.rate))) for
i in range(len(self.imageMoonRaw))]
self.MoonRect = [image.get_rect() for image in self.imageMoonRaw]
self.rect = self.imageRaw.get_rect()
self.imageRaw = py.transform.smoothscale(self.imageRaw,
(int(self.rect.width / self.rate), int(self.rect.width / self.rate)))
self.rect = self.imageRaw.get_rect()
self.y = self.rect.height
self.image = self.imageRaw.copy()
self.time = 0
def update(self, game_time, screen):
hour, minute = divmod(game_time, 60)
if hour <= 9:
self.time += 1
self.image = py.transform.rotate(self.imageRaw, self.time).convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = (int(screen.get_width() / 2), int(self.y / 2))
elif 17 >= hour > 9:
self.image = self.imageMoon[int(hour) - 10]
self.rect = self.MoonRect[int(hour) - 10]
self.rect.center = (int(screen.get_width() / 2), int(self.y / 2))This module realizes the rotation of the sun , The formation of the moon .
Logic modules
# Mobile game lens
def moveCamera(cube, floor, screen):
rate = 0.70 # The trigger area of lens movement is the right end of the screen (1-rate)/2 Range , And the left end of the screen (1-rate)/2 Range
triggerPointRight = screen.get_width() * (0.5 + rate / 2)
triggerPointLeft = screen.get_width() * (1 - rate) / 2
if cube.cube_body.rect.right > triggerPointRight:
if floor.floorTop[0].left > -floor.roadLength * floor.width + screen.get_width():
floor.updatePre(cube, triggerPointRight)
elif cube.cube_body.rect.left < triggerPointLeft:
if floor.floorTop[-1].right < floor.roadLength * floor.width:
floor.updatePre(cube, triggerPointLeft)
floor.update(screen)This module is used to control the movement of the screen lens , The so-called movement is actually relative , Because the screen itself will not move, it can only be achieved by moving the floor , Trigger movement is detected by creating two boundary lines , Here I also set the case that the lens cannot be moved , So as not to exceed the generation limit of the game floor .
Start the program :
# Time in 0-34 Between
def ControlTime():
gameTime = np.floor(py.time.get_ticks() / 1000)
hour = np.floor(gameTime / 60)
if hour <= 34:
return gameTime
elif hour > 34:
loop, timeM = divmod(gameTime, 34 * 60)
return timeM
def Main():
py.init()
FPS = 200 # Game frame rate
width, height = 800, 600 # Set window size
py.display.set_caption('THE BOX') # Set the game name
screen = py.display.set_mode((width, height))
clock = py.time.Clock()
backGround = BackgroundWall(screen)
bgGroup = py.sprite.Group()
bgGroup.add(backGround)
cube = CubeHuman(screen)
sunAndMoon = SunAndMoon()
sunGroup = py.sprite.Group()
sunGroup.add(sunAndMoon)
floor = FloorGen(screen, cube)
floor.generator(screen)
cloudGroup = py.sprite.Group()
cloudUpdateTimeLs = []
while True:
GameTime = ControlTime()
bgGroup.update(GameTime)
bgGroup.draw(screen)
sunGroup.update(GameTime, screen)
sunGroup.draw(screen)
cloudGen(cloudGroup, screen, GameTime, cloudUpdateTimeLs)
cloudGroup.update()
cloudGroup.draw(screen)
key = py.key.get_pressed() # Get keyboard input
cube.cube_body.KeyControl(key)
cube.cube_body.update(floor, screen)
moveCamera(cube, floor, screen) # Contains the ground refresh program
if key[py.K_ESCAPE]: exit() # Exit settings
for event in py.event.get():
if event.type == py.QUIT:
py.quit()
sys.exit()
''''''
cube.update(floor, screen)
py.display.update() # Refresh
clock.tick(FPS)
if __name__ == '__main__':
Main()
The sequence of creation and refresh of each module in the startup program must not be mistaken . The above is the whole content of this article , Based on past experience , I know this article is probably not read by many people , Mainly in case I want to pick it up one day , Pretend to write this article . Finally, give a lazy bag according to the Convention :The Box Extraction code :s2gj
PS: I'll revise it when I have time , For example, write a startup interface ( Annoyed ), Add a music bag or something , Automatically create a crawler or something ( laugh ), I may also write an AI to play instead of me ( sad ).
边栏推荐
- 借生态力量,openGauss突破性能瓶颈
- [netding cup 2020 rosefinch group]nmap 1 two solutions
- [target detection] yolov6 theoretical interpretation + practical test visdrone data set
- All in one 1329 cells (breadth first search)
- Realization of backstage brand management function
- openGauss之TryMe初体验
- STM32 small bug summary
- Using ecological power, opengauss breaks through the performance bottleneck
- Attack and defense world MFW
- 第2章 前台数据展现
猜你喜欢

Day4 --- flask blueprint and rest ful

Download and usage of sequel Pro

UVM入门实验1

IBM3650M4实体机安装VCenter7.0

OSI seven layer model and tcp/ip four layer (TCP and UDP) (notes)

Flutter 渲染机制——GPU线程渲染

Day5 - Flame restful request response and Sqlalchemy Foundation

缓存一致性与内存屏障
![[target detection] yolov6 theoretical interpretation + practical test visdrone data set](/img/ad/78835eea4decc15e0981e6561b875f.png)
[target detection] yolov6 theoretical interpretation + practical test visdrone data set

借生态力量,openGauss突破性能瓶颈
随机推荐
Realize SPU management in the background
Explain cache consistency and memory barrier
Attack and defense World Lottery
QPushButton 按钮的创建与简单应用
Day5 - Flame restful request response and Sqlalchemy Foundation
[MRCTF2020]PYWebsite 1
Breadth first search
[MRCTF2020]Ezpop 1
Supervisor 安装与使用
[target detection] yolov6 theoretical interpretation + practical test visdrone data set
Functions and arrow functions
My senior
Alibaba cloud international receipt message introduction and configuration process
After downloading URL loader and specifying the size of the image with limit, the image will not be displayed
The third letter to the little sister of the test | Oracle stored procedure knowledge sharing and test instructions
Creation and simple application of QPushButton button button
1024 | in the fourth year officially called Menon, the original intention is still there, and continue to move forward
Cenos7 update MariaDB
All in one 1251 - Fairy Island for medicine (breadth first search)
JS rotation chart