diff options
author | Aineopintojen-harjoitustyo-Algoritmit-j <github-hy-tiralabra@v.hix.fi> | 2024-01-29 02:34:32 +0200 |
---|---|---|
committer | Aineopintojen-harjoitustyo-Algoritmit-j <github-hy-tiralabra@v.hix.fi> | 2024-01-29 02:34:32 +0200 |
commit | e591d15abc4943a74c39f0fcab065213828a1514 (patch) | |
tree | bc22931abb08d8c213ba4eb81a85298d69e88890 /bots | |
parent | 0c034e6fbae0833f8524caf223deab450e9bb1b4 (diff) |
Updating too much. Renewed bots and tui.
Diffstat (limited to 'bots')
-rw-r--r-- | bots/__init__.py | 4 | ||||
-rw-r--r-- | bots/bad.py | 73 | ||||
-rw-r--r-- | bots/bot.py | 133 | ||||
-rw-r--r-- | bots/dssp.py | 80 | ||||
-rw-r--r-- | bots/idiot.py | 18 | ||||
-rw-r--r-- | bots/simple.py | 29 |
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 |