20151019 [KUASITC] Python遊戲(1)和(2)
  • 挑戰題(用 nc 或 telnet 連線)  IP : 203.64.91.58、Port : 88888889
 
 
[ 筆記內容 ] 
  • (一)遊戲規則
  • (二)過關畫面
  • (三)解題方法
  • 1.  猜數字大小原理
  • 2.  解題流程
  • (四)解題步驟與程式碼
  • [ 載入模組 ]
  • 1.  與目標電腦建立連線
  • 2. 接收並過濾 Server 上面顯示的訊息
  • 3.  確認訊息內容後,進入猜數字的流程
  • 4.  顯示猜到的數字,得到 Flag
  • 5.  關閉與目標電腦的連線
  • (五)完整程式碼 - Open Source
  • (六)執行結果
 
 
[ 參考資料 ]
 
 
[ GitHub ]
 
 

(一)遊戲規則

  1. 限時 25 秒 (普通題)、限時 5 秒 (挑戰題) 
  1. 範圍 1 ~ 1024
  1. 僅提示數字太大或數字太小
  1. 歡迎以各種奇技淫巧解決問題
 
 
 
 

(二)過關畫面

 
 
 
 

(三)解題方法

1.  猜數字大小原理
  • 因為要猜數字的範圍是 1 ~ 1024,除了在每一次的猜測時會出現提示之外,就沒有其他幫助解題的條件了。
  • 最大的數字是 1024,因為 1024 = 2^10 (1024 是 2 的 10 次方) ,所以最多要猜 10 次就可以知道答案;最小的數字是 1 ,1 = 2^0 (1 是 2 的 0 次方)
  • 如果要用最快又正當的方法猜到數字,我想到的只有:對切對切再對切 ... (總共切 10 次),即 (1/2)*(1/2)*(1/2)*(1/2)...
  • [ 示意圖 ]
  •  
 
2.  解題流程
  1. 與目標電腦建立連線
  1. 接收並過濾 Server 上面顯示的訊息 ( 過濾不必要的字元,如 "\r"、"\n" ... )
  1. 確認訊息內容後,進入猜數字的流程 
  1. STEP_1 : 先接收並過濾訊息
  1. STEP_2 : 送出 Enter 後,再重新接收並過濾訊息
  1. STEP_3 : 開始猜數字
  • 先設定數字種類(皆為變數) : 最大值、最小值、猜測值
  • 最大值 (預設為 : 1024)
  • 最小值 (預設為 : 1)
  • 猜測值 (預設為 : 512)
  • 再假設三種情況 : 猜太大、猜太小、猜中了
  • 如果猜太大
  • "最大值" 設定成目前的 "猜測值"
  • "最小值" 和目前的 "最大值" 相加後 / 2,並設成下一次的猜測值
  • 如果猜太小
  • "最小值" 設定成目前的 "猜測值"
  • "最小值" 和目前的 "最大值" 相加後 / 2,並設成下一次的猜測值
  • 如果猜中數字了
  • 直接略過猜數字的流程
  1. 顯示猜到的數字,得到 Flag 
  1. 關閉與目標電腦的連線
 
 
 
 

(四)解題步驟與程式碼

[ 載入模組 ]
import time            # 載入"time"模組
from socket import *   # 載入"socket"模組
 
 
1.  與目標電腦建立連線
# The Target IP:203.64.91.58,Port:8888/8889
 
TARGET = ('203.64.91.58', 8888)
sock = socket( AF_INET, SOCK_STREAM )
 
sock.connect(TARGET)        # 與目標電腦建立連線
 
 
2.  接收並過濾 Server 上面顯示的訊息
  • 過濾不必要的字元,如 "\r"、"\n" ...
#----------建立一個專門處理並過濾訊息的 Function----------
def bytes_to_str(recv):
    recv_b = recv.decode('utf-8')            # 將"bytes"解碼成"str"
 
    recv_b = recv_b.replace('\r', '')        # 過濾字串'\r' (把字串'\r'取代成'')
    recv_b = recv_b.replace('\n', '')        # 過濾字串'\n' (把字串'\n'取代成'')
 
    recv_b = bytes(str(recv_b).encode('utf-8'))        # 將"str"還原成"bytes"
    return recv_b        # 回傳處理後的訊息
#-------------------------END-------------------------
# ...
 
 
3.  確認訊息內容後,進入猜數字的流程
  • STEP_1 : 先接收並過濾訊息
if __name__ == '__main__':
# ...
    # STEP_1 : 先接收並過濾訊息
    recv = sock.recv(1024)    # 將 server 上的訊息傳給 recv (以 1024 bytes 接收 - bytes型態)
    recv = bytes_to_str(recv) # 將 recv 傳到 bytes_to_str() 用來過濾訊息,並將結果回傳給 recv
    print (recv)              # 顯示 recv 的內容   
# ...
 
  • STEP_2 : 送出 Enter 後,再重新接收並過濾訊息
if __name__ == '__main__':
# ...
    # STEP_2 : 送出 Enter 後,再重新接收並過濾訊息
    sock.send(bytes(str('\n').encode('utf-8')))    # 送出 Enter("\n") 字元
    recv = sock.recv(1024)       # 將 server 上的訊息傳給 recv (bytes型態)
    recv = bytes_to_str(recv)    # 用 bytes_to_str() 用來過濾訊息,並將結果回傳給 recv
# ...
 
  • STEP_3 : 開始猜數字
#-------- 猜數字的 Function--------
min_num = 1            # 設定初始的最小值(全域變數)
max_num = 1024         # 設定初始的最大值(全域變數)
guess_num = 512        # 設定初始的猜測值(全域變數)
 
def guess_number(recv):
        global min_num        # 引用最小值(全域變數)
        global max_num        # 引用最大值(全域變數)
        global guess_num      # 引用猜測值(全域變數)
        recv_b = recv.decode('utf-8')    # 將訊息以"utf-8"的格式解碼
        
        print(recv_b)
        
        # 根據接收訊息的內容來決定執行的動作(看看訊息裡面是否含有以下字串):      
        if 'big' in recv_b:        # 如果猜太大
                max_num = guess_num    # 把 "最大值" 設定成目前的 "猜測值"
                
                # 將 "最小值" 和目前的 "最大值" 相加後 / 2,並設成下一次的猜測值
                # 使用 int() 將 float 強制轉成 int 型態
                guess_num = int((min_num + max_num)/2)
 
        if 'small' in recv_b:      # 如果猜太小
                min_num = guess_num    # 把 "最小值" 設定成目前的 "猜測值"
                
                # 將 "最小值" 和目前的 "最大值" 相加後 / 2,並設成下一次的猜測值
                # 使用 int() 將 float 強制轉成 int 型態
                guess_num = int((min_num + max_num)/2)
 
        if 'Flag' in recv_b:       # 如果猜中了
                pass    # 直接略過猜數字的流程
 
        return guess_num    # 回傳目前猜測的數值
#-------------------------END-------------------------
# ...
#-------- main_Function --------
def main_function(recv):
 
        time.sleep(0.5)    # 設定延遲時間為 0.5 秒
        tmp = str(guess_number(recv))    # 進入 guess_number() 猜數字,並將結果回傳給 tmp    
        print(tmp)    # 列印目前正在猜測的數字
        sock.send(bytes(str(tmp + '\n').encode('utf-8')))    # 送出 Enter 
        
        return ''
#----------- END ------------
# ...
    # STEP_3 : 開始猜數字
if __name__ == '__main__':
# ...
        for i in range(10):    # 設定猜測次數(這裡為 10 次)            
                print(main_function(recv))    # 進入main_function()
                
                # 在每次猜測完數字後接收一次server的訊息(此時不用過濾)
                recv = sock.recv(1024)              
# ...   
 
 
4.  顯示猜到的數字,得到 Flag
# ...
if __name__ == '__main__':
# ...
    print(recv)    # 顯示最後得到的 Flag
 
 
5.  關閉與目標電腦的連線
# ...
if __name__ == '__main__':
# ...
    socket.close()    # 與目標電腦關閉連線
 
 
 
 

(五)完整程式碼 - Open Source

# Test_1.py and Test_2.py
import time
from socket import *
 
 
#-------- IP、Port --------
# The Target IP:203.64.91.58,Port:8888/8889
 
TARGET = ('203.64.91.58', 8888)    # 連線到普通題
# TARGET = ('203.64.91.58', 8889)  # 連線到挑戰題
 
sock = socket( AF_INET, SOCK_STREAM )
 
sock.connect(TARGET)        # 與目標電腦建立連線
#--------------------------------------
 
#-------- 猜數字的 Function--------
min_num = 1
max_num = 1024
guess_num = 512
 
def guess_number(recv):
        global min_num
        global max_num
        global guess_num
        recv_b = recv.decode('utf-8')
        
        print(recv_b)
 
        if 'big' in recv_b:        # 如果猜太大
                max_num = guess_num
                guess_num = int((min_num + max_num)/2)
 
        if 'small' in recv_b:        # 如果猜太小
                min_num = guess_num
                guess_num = int((min_num + max_num)/2)
 
        if 'Flag' in recv_b:        # 如果猜中了
                pass
 
        return guess_num
#----------- END ------------
 
#-------- 處理並過濾訊息的 Function --------
def bytes_to_str(recv):
        recv_b = recv.decode('utf-8')                # 將"bytes"解碼成"str"
 
        recv_b = recv_b.replace('\r', '')        # 過濾字串"\r"
        recv_b = recv_b.replace('\n', '')        # 過濾字串"\n"
 
        recv_b = bytes(str(recv_b).encode('utf-8'))        # 將"str"還原成"bytes"
        return recv_b        # 回傳處理後的訊息
#----------- END ------------
 
#-------- main_Function --------
def main_function(recv):
 
        time.sleep(0.5)
        tmp = str(guess_number(recv))        
        print(tmp)
        sock.send(bytes(str(tmp + '\n').encode('utf-8')))
        
        return ''
#----------- END ------------
 
if __name__ == '__main__':
        # STEP_1 : 先接收並過濾訊息
        recv = sock.recv(1024)
        recv = bytes_to_str(recv)
        print (recv)
 
 
        # STEP_2 : 送出 Enter 後,再重新接收並過濾訊息
        sock.send(bytes(str('\n').encode('utf-8')))
        recv = sock.recv(1024)
        recv = bytes_to_str(recv)
 
 
        # STEP_3 : 開始猜數字
        for i in range(10):                
                print(main_function(recv))
                recv = sock.recv(1024)
        
        print(recv)
#----------- END ------------
        sock.close()        # 與目標電腦關閉連線
 
 
 
 

(六)執行結果