Web Service #6 : Prisoner's Dilemma

     ตอนแรกผมตั้งใจจะเขียนเรื่องนี้เมื่อประมาณ 2 อาทิตย์ที่แล้ว แต่บังเอิญ .NET Beta 2 เพิ่งเปิดตัวพอดี ความสนใจทั้งหมดของผมเลยพุ่งไปอยู่ที่นั่น แล้วผมก็ไม่ได้นึกอะไรอีก จนกระทั่งมีนักศึกษาคนหนึ่งเขียนมาให้ผมช่วยแนะนำว่า Project ปีสุดท้ายจะทำเรื่องอะไรดี โดยที่เจ้าตัวเน้นว่าอยากได้เป็น .NET และเป็น Web Service ผมก็เลยยุส่งไปเลยว่าเขียนเกมส์ไปเลย ลองดู Prisoner's Dilemma เป็นตัวอย่าง เป็นเกมส์ที่เข้าใจง่ายๆ แต่สนุกมาก วันนี้ผมเลยจะเล่าเรื่องนี้ให้ฟัง

    Prisoner's Dilemma เป็นเกมส์ที่สร้างโดยใช้ Technology ของ Microsoft คือ .NET และ Web Service ผู้สร้างก็คือ Devx.com ร่วมมือกันกับ Microsoft จุดประสงค์ของการสร้างนี้ก็เพื่อเป็นการสอนการเขียนโปรแกรมกับ Web Service นั่นเอง จุดประสงค์คล้ายๆ กับ C-Robot ถ้าใครเขียนโปรแกรมมาไม่น้อยกว่า 5 ปี น่าจะรู้จักครับ

    ถ้าเปรียบเทียบ Prisoner's Dilemma กับ C-Robot แล้วคงต้องบอกว่า ห่างกันหลายขุมครับ Prisoner's Dilemma นั้นเรียบง่ายกว่ามากๆ แต่ก็ใช่ว่าจะไม่ซับซ้อนนะครับ มันอาจจะซับซ้อนกว่า C-Robot ก็ได้ ลองอ่านดูต่อไปก่อนครับ

แนวคิดของเกมส์

    สมมุติว่าคุณกับเพื่อนนี้ไปก่ออาชญากรรมร้ายแรงไว้เรื่องหนึ่ง สุดท้ายก็จนมุมหนีตำรวจไม่พ้น แต่ก็ยังไม่ถึงกับโชคร้ายไปเสียทั้งหมด ตำรวจเองไม่มีหลักฐานเพียงพอที่จะจับคุณทั้งสองเข้าห้องขัง ดังนั้นวิธีการของตำรวจจึงใช้วิธีทางจิตวิทยาเพื่อกล่อมคุณและเพื่อให้สารภาพ

    วิธีการของตำรวจนั้นก็คงไม่ใช่วิธีการอะไรใหม่นักหรอกครับ คือจับคุณทั้งสองแยกห้องสอบสวน ตำรวจให้ข้อเสนอที่เหมือนกันทั้งสองคน คือถ้าคุณยอมกล่าวหาว่าเพื่อของคุณเป็นผู้กระทำความผิด ตำรวจจะกันคุณเป็นพยาน แต่คุณก็มีทางเลือกอีกทางคืิอปฏิเสธทุกข้อกล่าวหา นั่นก็เป็นสิทธิของคุณ แต่แน่นอนครับ คุณหรือเพื่อนของคุณคงไม่มีวันสารภาพ ก็ตำรวจยังไงก็มีหลักฐานไม่พอที่จะเล่นงานคุณอยู่ดี

จากแนวคิดสู่ตัวเลข

คุณ เพื่อน คะแนนของคุณ คะแนนของเพื่อน
กล่าวหา กล่าวหา -1 -1
กล่าวหา ปฏิเสธ 2 -2
ปฏิเสธ กล่าวหา -2 2
ปฏิเสธ ปฏิเสธ 3 3

จะเห็นได้ว่าถ้าต่างกล่าวหาซึ่งกันและกัน ก็แบ่งรับความผิดกันไปครับ

แต่ถ้าคุณเป็นคนดีปฏิเสธว่าไม่ได้ทำ แต่เพื่อนของคุณแอบเล่นคุณซะแล้ว แบบนี้คุณก็ซวยคนเดียวครับ

แต่ถ้าคุณทั้งสองรักใคร่กันดี ไม่สนว่าตำรวจจะยื่นข้อเสนอที่ดีให้คุณอย่างไร ทั้งสองล้วนแล้วแต่ปฏิเสธทั้งคู่ ตำรวจก็จะทำอะไรคุณทั้งสองไม่ได้ สุดท้ายก็ต้องปล่อยตัวออกไปครับ

วิธีการเล่น

    วิธีการเล่นนั้น เกมส์นี้จะมี Web Service 3 ตัว คือ Web Service ของคุณ Web Service ของเพื่อนคุณ (User คนอื่น) และ Web Service ที่ทำหน้าที่เป็น Promoter จัดศึก (Ward หรือ Warden) ซึ่งเรื่องของตัว Promoter คุณไม่ต้องสนใจ มันอยู่ที่ Devx.com ครับ เรารู้เพียงหลักการว่า มันจะใช้วิธีการ Random เพื่อจับคู่จัดศึก คู่หนึ่งจะเล่นกันแค่ครั้งเดียว แล้วจะทำการ Random ใหม่ เล่นไปเรื่อยๆ จนถึงเวลาที่กำหนด ค่อยมาดูกันว่าใครได้คะแนนสูงสุด

     Web Service ของฝั่งคุณและเพื่อนเรียกว่า Bot ซึ่งมีเพียง Function เดียว เนื้อหาไม่มีอะไรมาก ตัวโปรโมตเตอร์มาเรียกฟังก์ชันนี้ที่คุณสร้าง ฟังก์ชันของคุณเพียงแค่ตอบออกไปว่า จะเลือกที่จะกล่าวหาหรือปฏิเสธ โดยใช้เลข 0 หรือ 1 เท่านั้น เท่านั้นจริงๆ ครับ เล่นง่ายนิดเดียว

กลยุทธ์ของเกมส์

    คุณอาจจะคิดว่าเกมส์บ้าอะไร แค่ตอบว่า 0 หรือ 1 มันจะสนุกยังไง ถึงมีกลยุทธ์ก็ไม่น่าจะมีอะไรยุ่งยาก ที่เหลือก็แล้วแต่ดวง มันขึ้นอยู่กับคู่ต่อสู้ด้วยว่าจะตอบว่าอย่างไร

    มันก็จริงครับ ถ้าเกมส์นี้คนเล่นกับคนนี่จะไม่มีความสนุกเลย แต่นี่เป็นโปรแกรมเล่นกับโปรแกรมครับ คุณต้องเขียนสูตรเป็นภาษาโปรแกรม และอย่างน้อยโปรแกรมที่ดีต้องเก็บสถิติเพื่อเอามาวิเคราะห์ครับ ถ้าคุณรู้ว่าคู่ต่อสู้เขียนโปรแกรมอย่างไร คุณจะได้เปรียบมาก ก็สูตรที่ใช้เขียนโปรแกรมมันตายตัว (ยกเว้นใช้ Random) บางคนนี่เก็บสถิติที่เกิดขึ้นโดยใช้ SQL Server จากนั้นเอามาวิเคราะ์ห์ให้รู้สูตรของฝั่งตรงกันข้าม

    เท่านี้ยังไม่พอครับ ตัว Ward ยอมให้ผู้เล่นหนึ่งคนสร้าง Bot ได้ถึง 100 ตัว ดังนั้นบางตัวเอาไว้รบกับ Bot คู่ต่อสู้ บางตัวเอาไว้เป็นบันไดให้กับ Bot ตัวอื่นของเราไต่ จะเห็นได้ว่าพลิกแพลงได้ไม่จำกัดครับ

 Implementation

Player Register

    เริ่มต้นที่ Site ของ Devx.com ครับ ที่เป็นจุดเริ่มต้นของเกมส์นี้ก็ที่ http://windowsxp.devx.com/PD/default.asp ลองเข้าไปอ่านดูครับ

    เมื่อคุณลองอ่านทุกอย่างเรียบร้อยแล้ว ให้คุณลงทะเบียนเป็นผู้เล่นเกมส์ที่ link ที่ชื่อว่า Player Registration ที่ Site ข้างต้น การลงทะเบียนนั้นจะใช้ข้อมูลของ Passport ถ้าคุณมี account ที่ Passport ก็จะลงทะเบียนได้ทันทีครับ แต่ถ้ายังไม่มีก็ให้ไปลงทะเบียนที่ Passport ก่อน ทุกอย่างฟรีครับ หลังจากที่คุณลงทะเบียนเสร็จจะเห็นจอลักษณะนี้ครับ

Welcome back, supojc!

Player ID:
2899B62C-5E8B-11D5-A20B-00902798DAA2
Your public identity code.
This is used for tracking opponent play history.


PIN:
2899B62B-5E8B-11D5-A20B-00902798DAA2
Your security key for bot registration and sign in.
Keep this code confidential to avoid sabotage.

 

สิ่งที่เราจะได้คือ GUID 2 ตัว ตัวแรกคือ Player ID ซึ่งเราจะใช้มันแทนชื่อของตัวเราครับ และ PIN เป็นรหัสลับที่เราต้องจำไว้

Create Proxy Class of Bot Registration

    ขั้นต่อไปคือคุณต้องสร้าง Proxy Class สำหรับการ Register Bot ของเรา เราใช้คำสั่ง

DOS Prompt

C:\CS> WebServiceUtil /command:proxy /path:http://209.1.14.205/Registration /Registration.asmx?sdl /out:cReg.cs /language:cs

cReg.cs

C:\cs>_

คำสั่งนี้ไปดึงเอา Interface ของ Web Service มาเป็นภาษา SDL จากนั้นเอามาสรุปสร้างเป็น Proxy Class ที่ชื่อว่า cReg.cs ใน Registration.asmx นั้นมี 2 Method คือ

SignIn Web Method
 

No additional reference information about the SignIn web method is available at this time.

Request parameters:

  • sPIN
  • iBotId
  • sBotName
  • sCallbackUrl
  • sProductId
  • Response type:

  • int

  • และ

    SignOut Web Method
     

    No additional reference information about the SignOut web method is available at this time.

    Request parameters:

  • sPIN
  • iBotId
  • Response type:

  • int
  • ทั้ง 2 Methods นี้เอาไว้ในการ Log on Bot เข้ากับ Warden และ Log off

    Creating Bot

        จากนั้นเราจะเอา proxy Class ของเรามา Modify เพื่อให้เป็น Bot อย่าเพิ่งงงนะครับ Bot ของเราเป็น Web Service ก็จริง มันมีนามสกุล .asmx แต่ Bot ของเราก็ถือว่า เป็น Client ของ Web Service ที่ชื่อว่า Registration.asmx ของ Warden เหมือนกับ เนื่องจาก Bot ของเราก็จำเป็นต้อง Logon และ Logoff จาก Warden เหมือนกัน

        เราจึงเอาแฟ้มข้อมูล Creg.cs มา Modify เป็น .asmx ครับ และ สร้าง Class ใหม่สำหรับ Web Service ที่ชื่อว่า Prisoner เพื่อเอาไว้เป็น Web Service ให้ Wardent มาเรียกใช้ อย่างงอีกนะครับ คือ Bot และ Warden นั้นเป็น ทั้ง Web Service Server และ Client ดังนี้ครับ

    Bot เป็น Client ของ Warden ในกรณีที่ Bot ต้องการ Register เข้าสู่ Warden

    Warden เป็น Client ของ Bot ในเวลาเล่นเกมส์ Warden จะมาถาม Bot ว่าเกมส์ที่จะเลีอกเล่นอะไร ระหว่าง การกล่าวหาหรือปฏิเสธ

    เรามาลองดูว่า Web Service ของ Bot เราดูบ้างว่ามี อะไรบ้าง

    GetMove

    Method นี้เอาไว้สำหรับ Warden มาเรียก เราต้องตอบกลับว่า 0 ถ้าต้องการปฎิเสธ แต่ถ้า ตอบกลับมาเป็น 1 เป็นการกล่าวหา
    Parameter ที่ส่งมานั้นคือ GUID ของคู่ต่อสู้ และ ตัวเลขประจำของ Bot (ผู้เล่นแต่ละคนสร้าง Bot ได้เต็มที่ 100 ตัว โดยต้องกำหนด iBotId ต่างกัน ตั้งแต่ 0 - 99)

    To test, click the 'Invoke' button.
    Parameter Value
    sOpponentPlayerID:
    iOpponentBotId:


    SaveScore

    หลังจากสู้กันเสร็จ Warden จะมาเรียก Web Service ของเราเพื่อป้อนผลการแข่งขัน

    To test, click the 'Invoke' button.
    Parameter Value
    sOpponentPlayerId:
    iOpponentBotId:
    iOpponentMove:
    iMyMove:
    iOpponentPoints:
    iMyPoints:

    -

    SignIn

    Log on Warden

    To test, click the 'Invoke' button.

     


    -

    SignOut

    Log out จาก Warden

    To test, click the 'Invoke' button.

     

    คุณอาจสงสัยว่า SignIn SignOut  เอาไว้ทำอะไร ตัว Warden ไม่น่าจะเป็นผู้เรียก SignIn SignOut จาก Bot ตัว Bot ต่างหากที่ต้องมี SignIn SignOut อันนี้เป็นกลยุทธ์ของ Web Service ที่ต่อไปจะเป็นเรื่องธรรมดามาก ผู้เรียก SignIn SignOut คือ คนครับ คนที่เป็นเจ้าของ Bot นั่นเอง เวลาต้องการ Start Bot ให้เข้าไป Browser เข้าไปที่ Site ของ Bot แล้วกดปุ่ม Invoke ส่วน SignOut ก็เช่นกันครับ ดูๆ ไปก็คล้ายกับเวลาที่เรา Start/Stop Service ของ Windows NT/2000 นะครับ

     

    เพื่อความเรียบง่าย ผมสร้าง Bot มาให้เรียบร้อยแล้วเป็นตัวอย่าง คุณไม่จำเป็น ต้องใช้ WebServiceUtil.exe เพื่อสร้าง Proxy Class เลย ตัวอย่างของ Bot มีดังนี้ครับ

    simple.asmx

    <%@ WebService Language="c#" class="Prisoner"%>
    using System;
    using System.Xml.Serialization;
    using System.Web.Services.Protocols;
    using System.Web.Services;
    
    [System.Web.Services.WebServiceBindingAttribute(Name="Registration2Soap", Namespace="http://tempuri.org/")]
    public class Registration : System.Web.Services.Protocols.SoapHttpClientProtocol {
        [System.Diagnostics.DebuggerStepThroughAttribute()]
        public Registration() {
            this.Url = "http://209.1.14.205/Registration/Registration.asmx";
        }
     
       [System.Diagnostics.DebuggerStepThroughAttribute()]
       [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/SignIn", 
            Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    	ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
       public int SignIn( string sPIN,  int iBotId,  string sBotName,  string sCallbackUrl,  string sProductId) {
            object[] results = this.Invoke("SignIn", new object[] {sPIN,
                        iBotId,
                        sBotName,
                        sCallbackUrl,
                        sProductId});
            return (int)(results[0]);
        }
        public System.IAsyncResult BeginSignIn(string sPIN, int iBotId, string sBotName, 
    		string sCallbackUrl, string sProductId, 
    		System.AsyncCallback callback, object asyncState) {
            return this.BeginInvoke("SignIn", new object[] {sPIN,
                        iBotId,
                        sBotName,
                        sCallbackUrl,
                        sProductId}, callback, asyncState);
        }
        public int EndSignIn(System.IAsyncResult asyncResult) {
            object[] results = this.EndInvoke(asyncResult);
            return (int)(results[0]);
        }
       
       [System.Diagnostics.DebuggerStepThroughAttribute()]
       [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/SignOut", 
    	Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    	ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
        public int SignOut( string sPIN,  int iBotId) {
            object[] results = this.Invoke("SignOut", new object[] {sPIN,
                        iBotId});
            return (int)(results[0]);
        }
        public System.IAsyncResult BeginSignOut(string sPIN, int iBotId, System.AsyncCallback callback, object asyncState) {
            return this.BeginInvoke("SignOut", new object[] {sPIN,
                        iBotId}, callback, asyncState);
        }
        public int EndSignOut(System.IAsyncResult asyncResult) {
            object[] results = this.EndInvoke(asyncResult);
            return (int)(results[0]);
        } 
    }
    public class Prisoner : System.Web.Services.WebService
    {
          [WebMethod]	    
          public int SignIn()
          {
             int iResult;
      	 Registration oRegService = new Registration();
    	 oRegService.Timeout = 10000;
    	 iResult = oRegService.SignIn(
    		"2899B62C-5E8B-11D5-A20B-00902798DAA2", 0,
    		"Power", 
    		"http://www22.blinker.com/supojc/power.asmx", "2899B62B-5E8B-11D5-A20B-00902798DAA2");
    		oRegService = null;
    		return iResult;
        	}
        
        	[WebMethod]
    	public int SignOut()
    	{
    		int iResult;
    		Registration oRegService = new Registration();
    		oRegService.Timeout = 10000;
    		iResult = oRegService.SignOut(
    			"2899B62C-5E8B-11D5-A20B-00902798DAA2", 
    			0);
    		oRegService = null;
    		return iResult;
        	}
            
    	[WebMethod]
    	public int GetMove(string sOpponentPlayerID, int iOpponentBotId)
    	{
    		Random r = new Random();
    		if (r.Next() % 100 < 80) {
    			return 1; // กล่าวหา
    		} else {
    			return 0; // ปฏิเสธ
    		}
    	}
        
    	[WebMethod]
    	public int SaveScore(string sOpponentPlayerId, 
    		int iOpponentBotId, int iOpponentMove, int iMyMove, 
    		int iOpponentPoints, int iMyPoints)
    	{
    		return 0;
            }
    }

    ข้อจำกัด

    ไม่อยากจะบอกเรื่องนี้เลยครับ ตัว Warden ของ Devx.com นั้นสร้างด้วย .NET Framework Beta 1 ซึ่งถ้าใครใช้กับ .NET Framework Beta 2 จะทำงานไม่ได้ครับ เพราะ Beta 1 ใช้ภาษา SDL ส่วน Beta 2 นั้นใช้ WSDL ซึ่งเข้ากันไม่ค่อยได้ครับ ดังนั้นถ้าใครอยากเล่นเกมส์ ต้อง Online Site ของตัวเอง Web Space ที่ให้เนื้อที่ Harddisk ฟรีๆ กลายเป็น Beta 2 หมดแล้วครับ