বিন্দু এবং বাক্সের জন্য দ্রুততম খেলোয়াড়


16

চ্যালেঞ্জটি হ'ল ক্লাসিক পেন্সিল এবং কাগজ গেম ডটস এবং বাক্সগুলির জন্য সলভার লিখুন । আপনার কোডটিতে দুটি পূর্ণসংখ্যা mএবং nইনপুট হিসাবে নেওয়া উচিত যা বোর্ডের আকার নির্দিষ্ট করে।

বিন্দুগুলির একটি খালি গ্রিড দিয়ে শুরু করে, খেলোয়াড়রা পালা নেয়, দুটি আনজাইনযুক্ত সংলগ্ন বিন্দুর মধ্যে একটি একক অনুভূমিক বা উল্লম্ব লাইন যুক্ত করে। এমন খেলোয়াড় যিনি 1 × 1 বাক্সের চতুর্থ দিকটি সম্পূর্ণ করেন তিনি এক পয়েন্ট অর্জন করেন এবং অন্যটি ঘুরেছেন। (পয়েন্টগুলি সাধারণত বক্সে প্লেয়ারের সনাক্তকারী চিহ্ন, যেমন একটি প্রাথমিক হিসাবে রেখে রেকর্ড করা হয়)। গেমটি শেষ হয় যখন আর কোনও লাইন স্থাপন করা যায় না। গেমের বিজয়ী সবচেয়ে পয়েন্ট সহ খেলোয়াড়।

এখানে চিত্র বর্ণনা লিখুন

হয় আপনি যে অনুমান করতে পারেন n = mবা n = m - 1এবং mঅন্তত 2।

চ্যালেঞ্জটি হ'ল solveএক মিনিটের মধ্যে সর্বাধিক সম্ভব ডটস এবং বক্সস গেমের কাছে। একটি গেমের আকার সহজভাবে n*m। আপনার কোডের আউটপুট হওয়া উচিত win, drawবা loseযা উভয় খেলোয়াড়ই সর্বোত্তমভাবে খেলবে তা ধরে নিয়ে প্রথম প্লেয়ারের ফলাফল হওয়া উচিত।

আপনার কোডটি সহজেই ইনস্টলযোগ্য এবং বিনামূল্যে সরঞ্জামগুলি ব্যবহার করে উবুন্টুতে অবশ্যই সংকলনযোগ্য / চলমান। সময়টির সাথে সাথে 1 মিনিটের মধ্যে আপনি নিজের কম্পিউটারে সমাধান করতে পারবেন এমন সবচেয়ে বড় অঞ্চল হিসাবে আপনার স্কোরটি জানান। তারপরে আমি আমার কম্পিউটারে কোডটি পরীক্ষা করব এবং একটি র‌্যাঙ্ক অর্ডার করা টেবিল তৈরি করব।

টাই-ব্রেকের ক্ষেত্রে, বিজয়ীর বৃহত্তম আকারের বোর্ডের এটি দ্রুততম কোড হবে যা এটি এক মিনিটের মধ্যে সমাধান করতে পারে।


কোডটি জিতে বা হারাতে না পারলেও আসল স্কোরটিও আউটপুট করা ভাল। এটি নির্ভুলতার একটি বিবেক পরীক্ষা করে তোলে।


2
আমাদের কি মিনিম্যাক্স ব্যবহার করতে হবে?
Qwr

@ কিউইউআর আপনি কি অন্য বিকল্পের মনে মনে বলতে পারেন?

অপেক্ষা করুন, কেবলমাত্র গ্রিডের আকারের ভিত্তিতে এই গেমটিতে অনুমানযোগ্য বিজয়ী আছে?
চার্লস

@ চার্লস হ্যাঁ যদি উভয় খেলোয়াড়ই অনুকূল খেলেন।

1
@ পিটারটেলর আমি মনে করি আপনি দুটি পয়েন্ট পাবেন তবে একটি মাত্র অতিরিক্ত পালা।

উত্তর:


15

সি 99 - 3x3 বোর্ড 0.084 এ

সম্পাদনা করুন: আমি আমার কোডটি রিফ্যাক্ট করেছি এবং ফলাফলগুলি সম্পর্কে আরও গভীর বিশ্লেষণ করেছি।

আরও সম্পাদনা: প্রতিসমগুলি দ্বারা ছাঁটাই যুক্ত করা হয়েছে। এটি 4 টি অ্যালগরিদম কনফিগারেশন তৈরি করে: আলফা-বিটা ছাঁটাইয়ের সাথে বা ছাড়া প্রতিসাম্যযুক্ত এক্স ছাড়া

সর্বাধিক সম্পাদনা: একটি হ্যাশ টেবিল ব্যবহার করে স্মৃতিচারণ যুক্ত হয়েছে, অবশেষে অসম্ভবটি অর্জন করেছে: একটি 3x3 বোর্ড সমাধান করা!

প্রাথমিক বৈশিষ্ট্য:

  • আলফা-বিটা ছাঁটাইয়ের সাথে মিনিম্যাক্সের সরাসরি বাস্তবায়ন
  • খুব সামান্য স্মৃতি ম্যানেজমেন্ট (কার্যকর অনুসন্ধানের dll বজায় রাখে; গাছের অনুসন্ধানে শাখা প্রতি ও (1) আপডেট)
  • প্রতিসম দ্বারা ছাঁটাই সহ দ্বিতীয় ফাইল। এখনও শাখা প্রতি ও (1) আপডেটগুলি অর্জন করে (প্রযুক্তিগতভাবে ও (এস) যেখানে এস প্রতিসম সংখ্যার সংখ্যা square এটি বর্গ বোর্ডের জন্য 7 এবং বর্গক্ষেত্র বোর্ডের জন্য 3)
  • তৃতীয় এবং চতুর্থ ফাইলগুলি মেমোয়েজেশন যুক্ত করে। হ্যাশটেবলের আকার ( #define HASHTABLE_BITWIDTH) এর উপরে আপনার নিয়ন্ত্রণ রয়েছে । এই আকারটি প্রাচীরের সংখ্যার চেয়ে বড় বা সমান হলে এটি কোনও সংঘর্ষ এবং ও (1) আপডেটের গ্যারান্টি দেয়। ছোট হ্যাশ টেবিলের আরও সংঘর্ষ হবে এবং কিছুটা ধীর হবে।
  • -DDEBUGপ্রিন্টআউটগুলির জন্য সংকলন করুন

সম্ভাব্য উন্নতি:

  • প্রথম সম্পাদনায় স্থির করা ছোট মেমরি ফাঁস
  • আলফা / বিটা ছাঁটাই দ্বিতীয় সম্পাদনায় যুক্ত করা হয়েছে
  • তৃতীয় সম্পাদনাতে ছাঁটাই করা প্রতিসাম্যগুলি (দ্রষ্টব্য যে প্রতিসাম্যগুলি মেমোজাইজেশন দ্বারা পরিচালিত হয় না , যাতে এটি পৃথক অপ্টিমাইজেশান থেকে যায়))
  • চতুর্থ সম্পাদনায় স্মৃতি যুক্ত করা হয়েছে
  • মেমোয়েজেশন প্রতিটি প্রাচীরের জন্য একটি সূচক বিট ব্যবহার করে। একটি 3x4 বোর্ডের 31 টি দেয়াল রয়েছে, তাই এই পদ্ধতি 4x4 বোর্ড পরিচালনা করতে পারে না সময় সীমাবদ্ধতা নির্বিশেষে। উন্নতি হবে এক্স-বিট পূর্ণসংখ্যার অনুকরণ করা, যেখানে কমপক্ষে প্রাচীরের সংখ্যার চেয়ে বড় X

কোড

সংগঠনের অভাবে ফাইলের সংখ্যা হাতছাড়া হয়ে গেছে। সমস্ত কোড এই গিথুব সংগ্রহস্থলে সরানো হয়েছে । স্মৃতিচারণ সম্পাদনাতে, আমি একটি মেকফিল এবং পরীক্ষার স্ক্রিপ্ট যুক্ত করেছি।

ফলাফল

ফাঁসির সময়গুলির প্লট লগ করুন

জটিলতার উপর নোটস

ব্রুট ফোর্স বিন্দু এবং বাক্সগুলিতে যোগাযোগ খুব দ্রুত জটিলতায় ফুটিয়ে তোলে ।

Rসারি এবং Cকলাম সহ একটি বোর্ড বিবেচনা করুন । আছে R*Cস্কোয়ার, R*(C+1)উল্লম্ব দেয়াল, এবং C*(R+1)অনুভূমিক দেয়াল। এটি মোট W = 2*R*C + R + C

যেহেতু লেম্বিক আমাদের মিনিম্যাক্স দিয়ে গেমটি সমাধান করতে বলেছিল, আমাদের গেম গাছের পাতাগুলিতে যেতে হবে। আসুন আপাতত ছাঁটাই উপেক্ষা করা যাক, কারণ কী বিষয়টিকে গুরুত্ব দেয় orders

আছে Wপ্রথম পদক্ষেপ জন্য বিকল্প। যারা প্রতিটি জন্য, পরবর্তী প্লেয়ারের কোনো বাজাতে পারেন W-1অবশিষ্ট দেয়াল, ইত্যাদি .. যে আমাদের একটি সার্চ-স্পেস দেয় SS = W * (W-1) * (W-2) * ... * 1, অথবা SS = W!। কারখানাগুলি বিশাল, তবে এটি কেবল শুরু। অনুসন্ধান স্পেসে পাতার নোডেরSS সংখ্যা । আমাদের বিশ্লেষণের সাথে আরও প্রাসঙ্গিক হ'ল মোট সিদ্ধান্তের সিদ্ধান্ত নেওয়া (যেমন গাছের শাখাগুলির সংখ্যা )। শাখাগুলির প্রথম স্তরের বিকল্প রয়েছে। তাদের প্রত্যেকের জন্য, পরবর্তী স্তরের ইত্যাদি রয়েছে etc. BWW-1

B = W + W*(W-1) + W*(W-1)*(W-2) + ... + W!

B = SUM W!/(W-k)!
  k=0..W-1

আসুন কয়েকটি ছোট টেবিলের আকারগুলি দেখুন:

Board Size  Walls  Leaves (SS)      Branches (B)
---------------------------------------------------
1x1         04     24               64
1x2         07     5040             13699
2x2         12     479001600        1302061344
2x3         17     355687428096000  966858672404689

এই সংখ্যাগুলি হাস্যকর হয়ে উঠছে। কমপক্ষে তারা ব্যাখ্যা করে যে ব্রুট-ফোর্স কোডটি 2x3 বোর্ডে চিরতরে স্থির থাকে বলে মনে হয়। 2x3 বোর্ডের অনুসন্ধানের স্থানটি 2x2 এর চেয়ে 742560 গুণ বড় । যদি 2x2 সম্পূর্ণ হতে 20 সেকেন্ড সময় নেয়, তবে একটি রক্ষণশীল এক্সট্রোপোলশন 2x3 এর জন্য 100 দিনের বেশি সময় কার্যকর করার পূর্বাভাস দেয় । স্পষ্টতই আমাদের ছাঁটাই করা দরকার।

ছাঁটাই বিশ্লেষণ

আমি আলফা-বিটা অ্যালগরিদম ব্যবহার করে খুব সাধারণ ছাঁটাই যুক্ত করে শুরু করেছি। মূলত, এটি অনুসন্ধান বন্ধ করে দেয় যদি কোনও আদর্শ বিরোধী এটির বর্তমান সুযোগগুলি কখনই না দেয়। "আরে দেখুন - আমি প্রতিদ্বন্দ্বী আমাকে প্রতি স্কয়ার পেতে দিলে আমি অনেকটা জিতেছি!", কখনও এআই ভাবেননি।

সম্পাদনা আমি প্রতিসম বোর্ডের উপর ভিত্তি করে ছাঁটাইও যুক্ত করেছি। আমি কোনও স্মৃতিচারণ পদ্ধতি ব্যবহার করি না, যদি কোনও দিন আমি স্মৃতি যুক্ত করি এবং সেই বিশ্লেষণটি আলাদা রাখতে চাই। পরিবর্তে, এটি এর মতো কাজ করে: বেশিরভাগ লাইনে গ্রিডের অন্য কোথাও একটি "প্রতিসম জুড়ি" থাকে। এখানে 7 টি প্রতিসাম্য রয়েছে (অনুভূমিক, উল্লম্ব, 180 ঘূর্ণন, 90 ঘূর্ণন, 270 ঘূর্ণন, তির্যক এবং অন্যান্য তির্যক) রয়েছে। সমস্ত 7 স্কোয়ার বোর্ডগুলিতে প্রয়োগ হয় তবে শেষ 4 টি বর্গাকার বোর্ডগুলিতে প্রযোজ্য না। প্রতিটি প্রাচীরের এই প্রতিটি প্রতিসাম্যের জন্য এটি "জোড়া" করার জন্য একটি পয়েন্টার রয়েছে। যদি, কোনও ঘুরে দেখা যায়, বোর্ড অনুভূমিকভাবে প্রতিসাম্যযুক্ত হয়, তবে প্রতিটি অনুভূমিক জোড়া কেবল খেলতে হবে।

স্মৃতি সম্পাদনা সম্পাদনা করুন! প্রতিটি প্রাচীর একটি অনন্য আইডি পায় যা আমি সুবিধামত একটি সূচক বিট হিসাবে সেট করেছি; নবম প্রাচীরের আইডি রয়েছে 1 << n। একটি বোর্ডের হ্যাশ, তখন সমস্ত দেয়ালের কেবল বাজানো হয়। এটি ও (1) সময়ে প্রতিটি শাখায় আপডেট করা হয়। হ্যাশটেবলের আকারটি একটিতে সেট করা হয় #define। সমস্ত পরীক্ষা 2 size 12 মাপ দিয়ে পরিচালিত হয়েছিল, কেন নয়? যখন হ্যাশ টেবিলকে ইনডেক্স করার বিটগুলির চেয়ে বেশি দেয়াল থাকে (এই ক্ষেত্রে 12 বিট), কমপক্ষে উল্লেখযোগ্য 12 টি মুখোশযুক্ত এবং সূচক হিসাবে ব্যবহৃত হয়। সংঘর্ষগুলি প্রতিটি হ্যাশটেবল সূচীতে একটি লিঙ্কযুক্ত তালিকার সাথে পরিচালিত হয়। নীচের চার্টটি কীভাবে হ্যাশটেবল আকারের কার্য সম্পাদনকে প্রভাবিত করে তার আমার দ্রুত ও মলিন বিশ্লেষণ। অসীম র‌্যামযুক্ত কম্পিউটারে আমরা সর্বদা টেবিলের আকারটি দেয়ালের সংখ্যায় সেট করে থাকতাম। একটি 3x4 বোর্ডের হ্যাশটেবল 2 ^ 31 দীর্ঘ হবে। হায়রে আমাদের সেই বিলাসিতা নেই।

হ্যাশটেবল আকারের প্রভাব

ঠিক আছে, ছাঁটাইতে ফিরে আসুন .. গাছের উঁচু অনুসন্ধান বন্ধ করে, আমরা পাতায় না গিয়ে প্রচুর সময় বাঁচাতে পারি । 'ছাঁটাই কারখানা' হ'ল আমাদের সম্ভাব্য শাখাগুলির ভগ্নাংশ যা আমাদের দেখতে হয়েছিল। ব্রুট-ফোর্সের একটি ছাঁটাই করার কারণ রয়েছে 1. এটি যত ছোট হবে তত ভাল।

শাখাগুলি লগ প্লট

ছাঁটাইয়ের উপাদানগুলির লগ প্লট করুন


সি-এর মতো দ্রুত ভাষার জন্য 23 এর দশকটি স্পষ্টতই ধীর বলে মনে হচ্ছে আপনি কি নিষ্ঠুরভাবে চাপ দিচ্ছেন?
qwr

আলফা বিটা থেকে অল্প পরিমাণে ছাঁটাই সহ ব্রুট ফোর্স। আমি সম্মত হই যে ২৩ এর দশক সন্দেহজনক, তবে আমি আমার কোডে কোনও কারণ দেখছি না যে এটি বেমানান হবে .. অন্য কথায়, এটি একটি রহস্য
ভুল

1
প্রশ্ন দ্বারা নির্দিষ্ট হিসাবে ইনপুট ফর্ম্যাট করা হয়। দুই শূণ্যস্থান দ্বারা বিভাজিত, পূর্ণসংখ্যার rows columnsবোর্ডের আকার উল্লেখ
wrongu

1
@ ল্যাম্বিক আমার মনে হয় না যে কিছু করার বাকি আছে। আমি এই ক্রেজি প্রজেক্ট দিয়ে শেষ করেছি!
غلطু

1
আমি মনে করি আপনার উত্তরটি একটি বিশেষ স্থানের দাবিদার। আমি এটি দেখেছিলাম এবং 3 বাই 3 হ'ল বৃহত্তম সমস্যার আকার যা এর আগে কখনও সমাধান হয়ে গেছে এবং আপনার কোডটি এর জন্য প্রায় তাত্ক্ষণিক। আপনি যদি 4 বা 4 দ্বারা 4 দ্বারা 4 টি সমাধান করতে পারেন তবে আপনি ফলাফলটি উইকি পৃষ্ঠাতে যুক্ত করতে এবং বিখ্যাত হতে পারেন :)

4

পাইথন - 29 এর দশকে 2x2

ধাঁধা থেকে ক্রস পোস্টিং । বিশেষত অপ্টিমাইজড নয়, তবে অন্যান্য প্রবেশকারীদের জন্য একটি দরকারী সূচনা পয়েন্ট তৈরি করতে পারে।

from collections import defaultdict

VERTICAL, HORIZONTAL = 0, 1

#represents a single line segment that can be drawn on the board.
class Line(object):
    def __init__(self, x, y, orientation):
        self.x = x
        self.y = y
        self.orientation = orientation
    def __hash__(self):
        return hash((self.x, self.y, self.orientation))
    def __eq__(self, other):
        if not isinstance(other, Line): return False
        return self.x == other.x and self.y == other.y and self.orientation == other.orientation
    def __repr__(self):
        return "Line({}, {}, {})".format(self.x, self.y, "HORIZONTAL" if self.orientation == HORIZONTAL else "VERTICAL")

class State(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.whose_turn = 0
        self.scores = {0:0, 1:0}
        self.lines = set()
    def copy(self):
        ret = State(self.width, self.height)
        ret.whose_turn = self.whose_turn
        ret.scores = self.scores.copy()
        ret.lines = self.lines.copy()
        return ret
    #iterate through all lines that can be placed on a blank board.
    def iter_all_lines(self):
        #horizontal lines
        for x in range(self.width):
            for y in range(self.height+1):
                yield Line(x, y, HORIZONTAL)
        #vertical lines
        for x in range(self.width+1):
            for y in range(self.height):
                yield Line(x, y, VERTICAL)
    #iterate through all lines that can be placed on this board, 
    #that haven't already been placed.
    def iter_available_lines(self):
        for line in self.iter_all_lines():
            if line not in self.lines:
                yield line

    #returns the number of points that would be earned by a player placing the line.
    def value(self, line):
        assert line not in self.lines
        all_placed = lambda seq: all(l in self.lines for l in seq)
        if line.orientation == HORIZONTAL:
            #lines composing the box above the line
            lines_above = [
                Line(line.x,   line.y+1, HORIZONTAL), #top
                Line(line.x,   line.y,   VERTICAL),   #left
                Line(line.x+1, line.y,   VERTICAL),   #right
            ]
            #lines composing the box below the line
            lines_below = [
                Line(line.x,   line.y-1, HORIZONTAL), #bottom
                Line(line.x,   line.y-1, VERTICAL),   #left
                Line(line.x+1, line.y-1, VERTICAL),   #right
            ]
            return all_placed(lines_above) + all_placed(lines_below)
        else:
            #lines composing the box to the left of the line
            lines_left = [
                Line(line.x-1, line.y+1, HORIZONTAL), #top
                Line(line.x-1, line.y,   HORIZONTAL), #bottom
                Line(line.x-1, line.y,   VERTICAL),   #left
            ]
            #lines composing the box to the right of the line
            lines_right = [
                Line(line.x,   line.y+1, HORIZONTAL), #top
                Line(line.x,   line.y,   HORIZONTAL), #bottom
                Line(line.x+1, line.y,   VERTICAL),   #right
            ]
            return all_placed(lines_left) + all_placed(lines_right)

    def is_game_over(self):
        #the game is over when no more moves can be made.
        return len(list(self.iter_available_lines())) == 0

    #iterates through all possible moves the current player could make.
    #Because scoring a point lets a player go again, a move can consist of a collection of multiple lines.
    def possible_moves(self):
        for line in self.iter_available_lines():
            if self.value(line) > 0:
                #this line would give us an extra turn.
                #so we create a hypothetical future state with this line already placed, and see what other moves can be made.
                future = self.copy()
                future.lines.add(line)
                if future.is_game_over(): 
                    yield [line]
                else:
                    for future_move in future.possible_moves():
                        yield [line] + future_move
            else:
                yield [line]

    def make_move(self, move):
        for line in move:
            self.scores[self.whose_turn] += self.value(line)
            self.lines.add(line)
        self.whose_turn = 1 - self.whose_turn

    def tuple(self):
        return (tuple(self.lines), tuple(self.scores.items()), self.whose_turn)
    def __hash__(self):
        return hash(self.tuple())
    def __eq__(self, other):
        if not isinstance(other, State): return False
        return self.tuple() == other.tuple()

#function decorator which memorizes previously calculated values.
def memoized(fn):
    answers = {}
    def mem_fn(*args):
        if args not in answers:
            answers[args] = fn(*args)
        return answers[args]
    return mem_fn

#finds the best possible move for the current player.
#returns a (move, value) tuple.
@memoized
def get_best_move(state):
    cur_player = state.whose_turn
    next_player = 1 - state.whose_turn
    if state.is_game_over():
        return (None, state.scores[cur_player] - state.scores[next_player])
    best_move = None
    best_score = float("inf")
    #choose the move that gives our opponent the lowest score
    for move in state.possible_moves():
        future = state.copy()
        future.make_move(move)
        _, score = get_best_move(future)
        if score < best_score:
            best_move = move
            best_score = score
    return [best_move, -best_score]

n = 2
m = 2
s = State(n,m)
best_move, relative_value = get_best_move(s)
if relative_value > 0:
    print("win")
elif relative_value == 0:
    print("draw")
else:
    print("lose")

পাইপি ব্যবহার করে 18 সেকেন্ড অবধি বাড়ানো যায়।

2

জাভাস্ক্রিপ্ট - 20 মিমি মধ্যে 1x2 বোর্ড

অনলাইন ডেমো এখানে উপলব্ধ (সতর্কতা - সম্পূর্ণ অনুসন্ধানের গভীরতার সাথে 1x2 এর চেয়ে বড় হলে খুব ধীর ): https://dl.rodboxusercontent.com/u/141246873/minimax/index.html

মূল জয়ের মানদণ্ডের জন্য তৈরি হয়েছিল (কোড গল্ফ) এবং গতির জন্য নয়।

উইন্ডোজ on-এ গুগল ক্রোম ভি 35 তে পরীক্ষিত।

//first row is a horizontal edges and second is vertical
var gameEdges = [
    [false, false],
    [false, false, false],
    [false, false]
]

//track all possible moves and score outcome
var moves = []

function minimax(edges, isPlayersTurn, prevScore, depth) {

    if (depth <= 0) {
        return [prevScore, 0, 0];
    }
    else {

        var pointValue = 1;
        if (!isPlayersTurn)
            pointValue = -1;

        var moves = [];

        //get all possible moves and scores
        for (var i in edges) {
            for (var j in edges[i]) {
                //if edge is available then its a possible move
                if (!edges[i][j]) {

                    //if it would result in game over, add it to the scores array, otherwise, try the next move
                    //clone the array
                    var newEdges = [];
                    for (var k in edges)
                        newEdges.push(edges[k].slice(0));
                    //update state
                    newEdges[i][j] = true;
                    //if closing this edge would result in a complete square, get another move and get a point
                    //square could be formed above, below, right or left and could get two squares at the same time

                    var currentScore = prevScore;
                    //vertical edge
                    if (i % 2 !== 0) {//i === 1
                        if (newEdges[i] && newEdges[i][j - 1] && newEdges[i - 1] && newEdges[i - 1][j - 1] && newEdges[parseInt(i) + 1] && newEdges[parseInt(i) + 1][j - 1])
                            currentScore += pointValue;
                        if (newEdges[i] && newEdges[i][parseInt(j) + 1] && newEdges[i - 1] && newEdges[i - 1][j] && newEdges[parseInt(i) + 1] && newEdges[parseInt(i) + 1][j])
                            currentScore += pointValue;
                    } else {//horizontal
                        if (newEdges[i - 2] && newEdges[i - 2][j] && newEdges[i - 1][j] && newEdges[i - 1][parseInt(j) + 1])
                            currentScore += pointValue;
                        if (newEdges[parseInt(i) + 2] && newEdges[parseInt(i) + 2][j] && newEdges[parseInt(i) + 1][j] && newEdges[parseInt(i) + 1][parseInt(j) + 1])
                            currentScore += pointValue;
                    }

                    //leaf case - if all edges are taken then there are no more moves to evaluate
                    if (newEdges.every(function (arr) { return arr.every(Boolean) })) {
                        moves.push([currentScore, i, j]);
                        console.log("reached end case with possible score of " + currentScore);
                    }
                    else {
                        if ((isPlayersTurn && currentScore > prevScore) || (!isPlayersTurn && currentScore < prevScore)) {
                            //gained a point so get another turn
                            var newMove = minimax(newEdges, isPlayersTurn, currentScore, depth - 1);

                            moves.push([newMove[0], i, j]);
                        } else {
                            //didnt gain a point - opponents turn
                            var newMove = minimax(newEdges, !isPlayersTurn, currentScore, depth - 1);

                            moves.push([newMove[0], i, j]);
                        }
                    }



                }


            }

        }//end for each move

        var bestMove = moves[0];
        if (isPlayersTurn) {
            for (var i in moves) {
                if (moves[i][0] > bestMove[0])
                    bestMove = moves[i];
            }
        }
        else {
            for (var i in moves) {
                if (moves[i][0] < bestMove[0])
                    bestMove = moves[i];
            }
        }
        return bestMove;
    }
}

var player1Turn = true;
var squares = [[0,0],[0,0]]//change to "A" or "B" if square won by any of the players
var lastMove = null;

function output(text) {
    document.getElementById("content").innerHTML += text;
}

function clear() {
    document.getElementById("content").innerHTML = "";
}

function render() {
    var width = 3;
    if (document.getElementById('txtWidth').value)
        width = parseInt(document.getElementById('txtWidth').value);
    if (width < 2)
        width = 2;

    clear();
    //need to highlight the last move taken and show who has won each square
    for (var i in gameEdges) {
        for (var j in gameEdges[i]) {
            if (i % 2 === 0) {
                if(j === "0")
                    output("*");
                if (gameEdges[i][j] && lastMove[1] == i && lastMove[2] == j)
                    output(" <b>-</b> ");
                else if (gameEdges[i][j])
                    output(" - ");
                else
                    output("&nbsp;&nbsp;&nbsp;");
                output("*");
            }
            else {
                if (gameEdges[i][j] && lastMove[1] == i && lastMove[2] == j)
                    output("<b>|</b>");
                else if (gameEdges[i][j])
                    output("|");
                else
                    output("&nbsp;");

                if (j <= width - 2) {
                    if (squares[Math.floor(i / 2)][j] === 0)
                        output("&nbsp;&nbsp;&nbsp;&nbsp;");
                    else
                        output("&nbsp;" + squares[Math.floor(i / 2)][j] + "&nbsp;");
                }
            }
        }
        output("<br />");

    }
}

function nextMove(playFullGame) {
    var startTime = new Date().getTime();
    if (!gameEdges.every(function (arr) { return arr.every(Boolean) })) {

        var depth = 100;
        if (document.getElementById('txtDepth').value)
            depth = parseInt(document.getElementById('txtDepth').value);

        if (depth < 1)
            depth = 1;

        var move = minimax(gameEdges, true, 0, depth);
        gameEdges[move[1]][move[2]] = true;
        lastMove = move;

        //if a square was taken, need to update squares and whose turn it is

        var i = move[1];
        var j = move[2];
        var wonSquare = false;
        if (i % 2 !== 0) {//i === 1
            if (gameEdges[i] && gameEdges[i][j - 1] && gameEdges[i - 1] && gameEdges[i - 1][j - 1] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][j - 1]) {
                squares[Math.floor(i / 2)][j - 1] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
            if (gameEdges[i] && gameEdges[i][parseInt(j) + 1] && gameEdges[i - 1] && gameEdges[i - 1][j] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][j]) {
                squares[Math.floor(i / 2)][j] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
        } else {//horizontal
            if (gameEdges[i - 2] && gameEdges[i - 2][j] && gameEdges[i - 1] && gameEdges[i - 1][j] && gameEdges[i - 1] && gameEdges[i - 1][parseInt(j) + 1]) {
                squares[Math.floor((i - 1) / 2)][j] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
            if (gameEdges[i + 2] && gameEdges[parseInt(i) + 2][j] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][j] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][parseInt(j) + 1]) {
                squares[Math.floor(i / 2)][j] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
        }

        //didnt win a square so its the next players turn
        if (!wonSquare)
            player1Turn = !player1Turn;

        render();

        if (playFullGame) {
            nextMove(playFullGame);
        }
    }

    var endTime = new Date().getTime();
    var executionTime = endTime - startTime;
    document.getElementById("executionTime").innerHTML = 'Execution time: ' + executionTime;
}

function initGame() {

    var width = 3;
    var height = 2;

    if (document.getElementById('txtWidth').value)
        width = document.getElementById('txtWidth').value;
    if (document.getElementById('txtHeight').value)
        height = document.getElementById('txtHeight').value;

    if (width < 2)
        width = 2;
    if (height < 2)
        height = 2;

    var depth = 100;
    if (document.getElementById('txtDepth').value)
        depth = parseInt(document.getElementById('txtDepth').value);

    if (depth < 1)
        depth = 1;

    if (width > 2 && height > 2 && !document.getElementById('txtDepth').value)
        alert("Warning. Your system may become unresponsive. A smaller grid or search depth is highly recommended.");

    gameEdges = [];
    for (var i = 0; i < height; i++) {
        if (i == 0) {
            gameEdges.push([]);
            for (var j = 0; j < (width - 1) ; j++) {
                gameEdges[i].push(false);
            }
        }
        else {
            gameEdges.push([]);
            for (var j = 0; j < width; j++) {
                gameEdges[(i * 2) - 1].push(false);
            }
            gameEdges.push([]);
            for (var j = 0; j < (width - 1) ; j++) {
                gameEdges[i*2].push(false);
            }
        }
    }

    player1Turn = true;

    squares = [];
    for (var i = 0; i < (height - 1) ; i++) {
        squares.push([]);
        for (var j = 0; j < (width - 1); j++) {
            squares[i].push(0);
        }
    }

    lastMove = null;

    render();
}

document.addEventListener('DOMContentLoaded', initGame, false);

ডেমো সত্যিই দুর্দান্ত! 3 এক্স 3 সত্যই আকর্ষণীয় হিসাবে আপনি অনুসন্ধানের গভীরতা বাড়ানোর সাথে সাথে বিজয়ীর পিছনে পিছনে পরিবর্তন হয়। আমি কি পরীক্ষা করতে পারি, আপনার মিনিম্যাক্সটি কোনও বাঁক দিয়ে কখনও অর্ধেক পথ থামায়? আমার অর্থ হ'ল যদি কেউ একটি বর্গক্ষেত্র পায় তবে এটি কি সর্বদা তাদের পালা শেষে প্রসারিত হয়?

2x2 3 ডট 3 দ্বারা 3 আপনি কি নিশ্চিত যে আপনার কোডটি 20 মিমিতে ঠিক এটি সমাধান করতে পারে?

"যদি কেউ একটি বর্গক্ষেত্র পায়, এটি কি সর্বদা তাদের পালা শেষে প্রসারিত হয়?" - যদি প্লেয়ারটি একটি বর্গক্ষেত্র পায়, তবে এটি পরবর্তী পালাতে চলে যায় তবে পরবর্তী পর্দাটি একই প্লেয়ারের জন্য অর্থাৎ তারা স্কোয়ার সম্পূর্ণ করার জন্য একটি অতিরিক্ত পালা পায়। "2x2 3 দ্বারা 3 বিন্দু" - ওফস। সেক্ষেত্রে আমার স্কোর 1x1।
rdans
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.