summaryrefslogtreecommitdiff
path: root/bots
diff options
context:
space:
mode:
authorAineopintojen-harjoitustyo-Algoritmit-j <github-hy-tiralabra@v.hix.fi>2024-01-29 02:34:32 +0200
committerAineopintojen-harjoitustyo-Algoritmit-j <github-hy-tiralabra@v.hix.fi>2024-01-29 02:34:32 +0200
commite591d15abc4943a74c39f0fcab065213828a1514 (patch)
treebc22931abb08d8c213ba4eb81a85298d69e88890 /bots
parent0c034e6fbae0833f8524caf223deab450e9bb1b4 (diff)
Updating too much. Renewed bots and tui.
Diffstat (limited to 'bots')
-rw-r--r--bots/__init__.py4
-rw-r--r--bots/bad.py73
-rw-r--r--bots/bot.py133
-rw-r--r--bots/dssp.py80
-rw-r--r--bots/idiot.py18
-rw-r--r--bots/simple.py29
6 files changed, 228 insertions, 109 deletions
diff --git a/bots/__init__.py b/bots/__init__.py
index f180c3a..f346d07 100644
--- a/bots/__init__.py
+++ b/bots/__init__.py
@@ -1,4 +1,4 @@
""" bots - tämä alimoduli tarjoaa tekoälyn """
-from .bot import Bot
-from .bad import BadBot
+from .simple import SimpleBot
+from .dssp import DSSPBot
diff --git a/bots/bad.py b/bots/bad.py
deleted file mode 100644
index 5197dc9..0000000
--- a/bots/bad.py
+++ /dev/null
@@ -1,73 +0,0 @@
-""" bots/bad.py - botti joka ehkä osaa merkata jonkun asian """
-from random import sample
-from tui import Action
-from .bot import Bot
-
-class BadBot(Bot):
- """ IdiotBot - merkistsee kaikki turvallisiksi avata """
- # pylint: disable = too-few-public-methods
- def missing_bombs(self, matrix, x, y):
- """ test how many boms are not found at the coordinate """
- dx = len(matrix)
- dy = len(matrix[0])
- bcount = 0
- for nx, ny in self.neighbours(dx, dy, x, y):
- if matrix[nx][ny] in (9,11):
- bcount+=1
- return matrix[x][y]-bcount
-
- def get_tiles_at_border(self, matrix):
- """ get interesting tiles on the border of cleared and masked area """
- tiles = []
- w = len(matrix)
- h = len(matrix[0])
- for y in range(h):
- for x in range(w):
- if matrix[x][y] < 12:
- open_tiles=1
- masked_tiles=0
- else:
- open_tiles=0
- masked_tiles=1
- for nx, ny in self.neighbours(w, h, x, y):
- if matrix[nx][ny] < 12:
- open_tiles+=1
- else:
- masked_tiles+=1
- if open_tiles and masked_tiles:
- tiles.append( (x,y) )
- return tiles
-
- def get_unopened_tiles(self, matrix):
- """ get interesting tiles on the border of cleared and masked area """
- tiles = []
- w = len(matrix)
- h = len(matrix[0])
- for y in range(h):
- for x in range(w):
- if matrix[x][y] == 12:
- tiles.append( (x,y) )
- return tiles
-
- def hint(self, matrix, cursor_x, cursor_y):
- """ merkitsee jonkin ruudun """
- super().hint(matrix, cursor_x, cursor_y)
- w = len(matrix)
- h = len(matrix[0])
- # pylint: disable = consider-using-enumerate
- for x, y in self.get_tiles_at_border(matrix):
- ncoords=self.neighbours(w,h,x,y)
- ntiles=self.coordinates_to_tiles(matrix,ncoords)
- unopened=ntiles.count(12)
- bombs=ntiles.count(10)
- if unopened:
- if matrix[x][y]<9 and matrix[x][y]==bombs:
- safe = ncoords[ntiles.index(12)]
- return(Action.SAFE, safe[0], safe[1])
- if matrix[x][y]-bombs==unopened:
- bomb = ncoords[ntiles.index(12)]
- return(Action.BOMB, bomb[0], bomb[1])
- if self.uncertain:
- x, y = sample(self.get_unopened_tiles(matrix),1)[0]
- return (Action.OPEN, x, y)
- return (Action.NOOP, cursor_x, cursor_y)
diff --git a/bots/bot.py b/bots/bot.py
index 5ad0bd2..49bd962 100644
--- a/bots/bot.py
+++ b/bots/bot.py
@@ -1,32 +1,133 @@
""" bots/bot.py - bottien kantaisä """
from tui import Action
+from board import Tile
class Bot():
""" Bot - perusluokka perittäväksi """
def __init__(self, **opts):
self.uncertain = opts['uncertain'] if 'uncertain' in opts else False
- self.hints = 0
+ self.safe_tiles = set()
+ self.bomb_tiles = set()
+ self.matrix = []
+ self.w = 0
+ self.h = 0
- def neighbours(self,dx,dy,x,y):
- """ palauttaa listana viereiset koordinaatit """
+ def search(self):
+ """ search - etsii pommeja tai vapaita ko joukkoihin """
+ return False
+
+ def lucky_guess(self):
+ """ lucky_guess - lisää yhden arvatun vapaan vapaiden joukkoon """
+ return Action.NOOP, 0, 0
+
+ def get_hint_from_list(self):
+ """ palauttaa vihjeen suoraan listalta """
+ if self.safe_tiles:
+ x, y = self.safe_tiles.pop()
+ return Action.SAFE, x, y
+ if self.bomb_tiles:
+ x, y = self.bomb_tiles.pop()
+ return Action.BOMB, x, y
+ return Action.NOOP, 0, 0
+
+ def saved_hints(self):
+ """ onko muuveja varastossa """
+ return self.safe_tiles or self.bomb_tiles
+
+ def hint(self, matrix, cursor_x, cursor_y):
+ """ antaa vinkin. tässä tapauksessa ei mitään """
+ self.matrix = matrix
+ self.w, self.h = self.get_dimensions()
+
+ if self.saved_hints():
+ return self.get_hint_from_list()
+ if self.search():
+ return self.get_hint_from_list()
+ if self.uncertain and self.lucky_guess():
+ return self.get_hint_from_list()
+ return Action.NOOP, cursor_x, cursor_y
+
+ def get_dimensions(self):
+ """ palauttaa matriisin dimensiot """
+ return len(self.matrix), len(self.matrix[0])
+
+ def get_neighbours(self, tile):
+ """ palauttaa viereiset koordinaatit joukkona """
+ x, y = tile
offsets = (
(-1, -1), ( 0, -1), ( 1, -1),
(-1, 0), ( 1, 0),
(-1, 1), ( 0, 1), ( 1, 1),
)
- coords=[]
+ tiles=set()
for ox, oy in offsets:
- if ox+x in range(dx):
- if oy+y in range(dy):
- coords.append((ox+x, oy+y))
- return coords
+ if ox+x in range(self.w):
+ if oy+y in range(self.h):
+ tiles.add((ox+x, oy+y))
+ return tiles
- def coordinates_to_tiles(self, matrix, coords):
- """ lukee koordinaateissa olevien ruutujen arvot listaksi """
- return [matrix[x][y] for x,y in coords]
+ def get_value(self, tile):
+ """ palauttaa laatan arvon """
+ return self.matrix[tile[0]][tile[1]]
- def hint(self, matrix, cursor_x, cursor_y):
- """ antaa vinkin. tässä tapauksessa ei mitään """
- # pylint: disable = unused-argument
- self.hints += 1
- return Action.NOOP, cursor_x, cursor_y
+ def remove_number_tiles(self, tiles):
+ """ poistaa vapaat ja vapaaksi merkityt alueet ja numerolaatat """
+ for tile in list(tiles):
+ if self.matrix[tile[0]][tile[1]] < Tile.FLAG_BOMB:
+ tiles.remove(tile)
+
+ def remove_bomb_tiles(self, tiles):
+ """ poistaa pommit ja pommiksi merkityt """
+ count=0
+ for tile in list(tiles):
+ if self.matrix[tile[0]][tile[1]] in (Tile.BOMB, Tile.FLAG_BOMB):
+ tiles.remove(tile)
+ count+=1
+ return count
+
+ def known_tile(self, tile):
+ """ tutkii onko laatta tiedetty """
+ return self.matrix[tile[0]][tile[1]] < Tile.UNOPENED
+
+ def number_tile(self, tile):
+ """ tutkii onko numerolaatta """
+ return 0 < self.matrix[tile[0]][tile[1]] < Tile.BOMB
+
+ def count_unknowns(self, tiles):
+ """ laskee tunnistamattomat laatat """
+ count=0
+ for tile in list(tiles):
+ if not self.known_tile(tile):
+ count+=1
+ return count
+
+ def remove_unknowns(self, tiles):
+ """ poistaa tunnistamattomat laatat """
+ count=0
+ for tile in list(tiles):
+ if not self.known_tile(tile):
+ tiles.remove(tile)
+ count+=1
+ return count
+
+ def get_interesting_tiles(self):
+ """ palauttaa laatat joiden naapureissa on vaihtelua """
+ tiles = set()
+ for x in range(self.w):
+ for y in range(self.h):
+ if self.number_tile((x,y)):
+ n = self.get_neighbours((x,y))
+ l = len(n)
+ r = self.count_unknowns(n)
+ if r in range(1,l-1):
+ tiles.add((x,y))
+ return tiles
+
+ def get_unknown_tiles(self):
+ """ palauttaa kaikki tuntemattomat laatat """
+ tiles = set()
+ for x in range(self.w):
+ for y in range(self.h):
+ if not self.known_tile((x,y)):
+ tiles.add((x,y))
+ return tiles
diff --git a/bots/dssp.py b/bots/dssp.py
new file mode 100644
index 0000000..33578a0
--- /dev/null
+++ b/bots/dssp.py
@@ -0,0 +1,80 @@
+""" bots/dssp.py - päättelee kahden vierekkäisen laatan perusteella """
+from random import sample
+from .simple import SimpleBot
+
+class DSSPBot(SimpleBot):
+ """ DSSPBot - perustyhmä botti """
+
+ def search(self):
+ """ search - etsii kahden vierekkäisen laatan perusteella"""
+ if super().search():
+ return True
+ tiles = list(self.get_interesting_tiles())
+ pairs = []
+ # pylint: disable = consider-using-enumerate
+ for i in range(len(tiles)):
+ for j in range(i+1,len(tiles)):
+ if abs(tiles[i][0]-tiles[j][0])==1 or abs(tiles[i][1]-tiles[j][1])==1:
+ pairs.append((tiles[i],tiles[j]))
+ pairs.append((tiles[j],tiles[i]))
+
+ for tile1, tile2 in pairs:
+ c1 = self.get_value(tile1)
+ c2 = self.get_value(tile2)
+ n1 = self.get_neighbours(tile1)
+ n2 = self.get_neighbours(tile2)
+ self.remove_number_tiles(n1)
+ self.remove_number_tiles(n2)
+ c1 -= self.remove_bomb_tiles(n1)
+ c2 -= self.remove_bomb_tiles(n2)
+
+ # otetaan vain alue1:n laatat pois vähennetään se pommeista
+ # näin tiedetään montako pommia on jäätävä yhteiselle alueelle
+ nc = n1 & n2
+ n1 = n1 - nc
+ n2 = n2 - nc
+ cc = c1 - len(n1)
+
+ # jos yhteiselle alueelle ei jääkkään pommeja
+ if cc < 1:
+ continue
+
+ # vähennetään yhteinen alue ja sen pommit alueesta 2
+ # jos jäljelle ei jää pommeja merkataan seiffeiks
+ # jos avaamattomia pommien määrä merkataan pommeiks
+ c2 -= cc
+
+ if c2 == 0:
+ for safe in n2:
+ self.safe_tiles.add(safe)
+ if cc == len(nc) and c2 == len(n2):
+ for bomb in n2:
+ self.bomb_tiles.add(bomb)
+
+ return self.saved_hints()
+
+ def lucky_guess(self):
+ heatmap = dict.fromkeys(self.get_unknown_tiles(), 0)
+ tiles = self.get_interesting_tiles()
+ for tile in tiles:
+ n = self.get_neighbours(tile)
+ c = self.get_value(tile) - self.remove_bomb_tiles(n)
+ self.remove_number_tiles(n)
+ for tile in n:
+ heatmap[tile] = max( heatmap[tile], c/len(n) )
+
+ for tile, value in heatmap.items():
+ if value>0:
+ continue
+ if tile[0] in range(1,self.w-1):
+ heatmap[tile]+=0.05
+ if tile[1] in range(1,self.h-1):
+ heatmap[tile]+=0.05
+
+ best = min((x for _, x in heatmap.items()))
+ best_tiles = [x for x,y in heatmap.items() if y == best]
+
+ if best_tiles:
+ self.safe_tiles.add(sample(best_tiles,1)[0])
+ return True
+ return False
diff --git a/bots/idiot.py b/bots/idiot.py
deleted file mode 100644
index 933eee9..0000000
--- a/bots/idiot.py
+++ /dev/null
@@ -1,18 +0,0 @@
-""" bots/idiot.py - se ensimmäinen botti joka tekee kaiken väärin """
-from tui import Action
-from .bot import Bot
-
-class IdiotBot(Bot):
- """ IdiotBot - merkistsee kaikki turvallisiksi avata """
- # pylint: disable = too-few-public-methods
-
- def hint(self, matrix, cursor_x, cursor_y):
- """ merkitsee jonkin ruudun """
- super().hint(matrix, cursor_x, cursor_y)
- # pylint: disable = consider-using-enumerate
- for ty in range(len(matrix[0])):
- for tx in range(len(matrix)):
- if matrix[tx][ty]==12:
- return(Action.SAFE, tx, ty)
- return (Action.NOOP, cursor_x, cursor_y)
- \ No newline at end of file
diff --git a/bots/simple.py b/bots/simple.py
new file mode 100644
index 0000000..cb3d25c
--- /dev/null
+++ b/bots/simple.py
@@ -0,0 +1,29 @@
+""" bots/simple.py - yksinkertainen botti joka etsii vain yhdeltä laatalta """
+from random import sample
+from .bot import Bot
+
+class SimpleBot(Bot):
+ """ SimpleBot - perustyhmä botti """
+
+ def search(self):
+ """ simple_search - jos viereisten avaamattomien määrä tästmää """
+ tiles = self.get_interesting_tiles()
+ for tile in tiles:
+ c = self.get_value(tile)
+ n = self.get_neighbours(tile)
+ self.remove_number_tiles(n)
+ c -= self.remove_bomb_tiles(n)
+ if c == 0:
+ for safe in n:
+ self.safe_tiles.add(safe)
+ if c == len(n):
+ for bomb in n:
+ self.bomb_tiles.add(bomb)
+ return self.saved_hints()
+
+ def lucky_guess(self):
+ tiles = self.get_unknown_tiles()
+ if tiles:
+ self.safe_tiles.add(sample(sorted(tiles),1)[0])
+ return True
+ return False