-
Notifications
You must be signed in to change notification settings - Fork 0
/
mapkov.py
177 lines (130 loc) · 6.11 KB
/
mapkov.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/env python
# -*- coding: utf-8 -*- #
""" Builds a multicolored map of tiles using a Markov chain algorithm.
Each word in the sample text corresponds to a single RGB color.
Using the frequency table we make from the text, we can procedurally
generate a map of colored tiles that follows the same pattern.
Basically a way of visualing the patterns in a block of text.
It's intended for use procedurally generating maps in some kind of 2D game. """
import random
from math import ceil
import pygame
# Size of screen
width = 300
height = 300
# Sample text
sampleText = ("Stop. Who would cross the Bridge of Death must answer me these qu"
"estions three, ere the other side he see. Ask me the questions, b"
"ridgekeeper. I am not afraid. What... is your name? My name is Si"
"r Lancelot of Camelot. What... is your quest? To seek the Holy Gr"
"ail. What... is your favourite colour? Blue. Go on. Off you go. O"
"h, thank you. Thank you very much. That's easy. Stop. Who would c"
"ross the Bridge of Death must answer me these questions three, er"
"e the other side he see. Ask me the questions, bridgekeeper. I'm "
"not afraid. What... is your name? Sir Robin of Camelot. What... i"
"s your quest? To seek the Holy Grail. What... is the capital of A"
"ssyria? [pause] I don't know that. [he is thrown over the edge in"
"to the volcano] Auuuuuuuugh. Stop. What... is your name? Sir Gala"
"had of Camelot. What... is your quest? I seek the Grail. What... "
"is your favourite colour? Blue. No, yel... [he is also thrown ove"
"r the edge] auuuuuuuugh. Hee hee heh. Stop. What... is your name?"
"It is 'Arthur', King of the Britons. What... is your quest? To s"
"eek the Holy Grail. What... is the air-speed velocity of an unlad"
"en swallow? What do you mean? An African or European swallow? Huh"
"? I... I don't know that. [he is thrown over] Auuuuuuuugh. How do"
"know so much about swallows? Well, you have to know these things"
"when you're a king, you know.")
def buildFrequencyTable(text):
""" Pair each word in the sample text with what word(s) are most likely to come after it. """
# Turn the input text into a list of words/tokens
sourceList = text.split()
# Start our model as an empty dictionary
markovModel = {}
# Iterate through the sourceList to build our frequency table
for i, word in enumerate(sourceList):
# Check if we're at the end of the list
if i+1 != len(sourceList):
# If we haven't added this word to our table yet, add it
if word not in markovModel:
markovModel[word] = {sourceList[i+1]: 1}
# If we have added this word to our table, record what word comes after it
if word in markovModel:
if sourceList[i+1] not in markovModel[word]:
markovModel[word][sourceList[i+1]] = 1
else:
markovModel[word][sourceList[i+1]] += 1
# If there is no following word
else:
markovModel[word] = {}
return markovModel
def getColorFromString(baseString):
""" Take an arbitrary string and return a RGB tuple from the first 3 bytes. """
# Get byte array of string
stringHash = 0
# Hash it together using the sdbm algorithm
for c in baseString:
stringHash = stringHash * (ord(c) - 1) * 65599 + ord(c)
rgb = [0, 0, 0]
# Split it up into 3 separate values
for i in range(3):
rgb[i] = int((stringHash >> (i * 8)) & 0xFF)
return tuple(rgb)
def findNextWord(currentWord, markovModel):
""" Use our frequency table to get the next word in sequence. """
print(currentWord)
# If this line was
if not markovModel[currentWord]:
while not markovModel[currentWord]:
currentWord = random.sample(markovModel.items(), 1)[0][0]
potentialWords = []
# For each potential next word's multiplicity, add it to the potentialWords list that many times
# It's how we represent some words being more frequent than others
for word in markovModel[currentWord]:
for i in range(markovModel[currentWord][word]):
potentialWords.append(word)
# Pick something out of the potentialWords list to be our next word
nextWord = random.choice(potentialWords)
return nextWord
def drawTile(screen, markovModel, word, coordinates):
""" Draws a colored tile on the screen at the specified coordinates. """
# Find our next word and its respective color
word = findNextWord(word, markovModel)
color = getColorFromString(word)
# Draw the tile
pygame.draw.rect(screen, color, pygame.Rect(coordinates[0], coordinates[1], 5, 5))
pygame.display.flip()
# Change x and y so the next square is in the right place.
# If we're at the edge of the screen, loop to the next row.
coordinates[0] += 5
if coordinates[0] > width:
coordinates[0] = 0
coordinates[1] += 5
print(word, coordinates)
return [word, coordinates]
def main():
""" Creates pygame window and runs the main process """
# Create screen
pygame.init()
screen = pygame.display.set_mode((width, height))
screen.fill((0, 0, 0))
# Build our Markov table
markovModel = buildFrequencyTable(sampleText)
# Find how many squares we can fit on our screen
numberOfSquares = ceil(((width * height) / 25) + (width / 5))
# Pick a random starting value
word = random.sample(markovModel.items(), 1)[0][0]
# Our first square will be at 0,0
coordinates = [0, 0]
for i in range(numberOfSquares):
tile = drawTile(screen, markovModel, word, coordinates)
word = tile[0]
coordinates = tile[1]
print("Done!")
# Keep pygame going until we close it
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if __name__ == "__main__":
main()