Note
Go to the end to download the full example code.
国际象棋大师#
MultiDiGraph
类的一个示例。
函数 chess_pgn_graph
读取存储在指定 PGN 文件(PGN = “便携式游戏标记”)中的一系列国际象棋比赛。这里使用的(压缩)默认文件:
chess_masters_WCC.pgn.bz2
包含从 1886 年到 1985 年的所有 685 场世界国际象棋锦标赛比赛。(数据来自 http://chessproblem.my-free-games.com/chess/games/Download-PGN.php)
chess_pgn_graph()
函数返回一个带有多个边的 MultiDiGraph
。每个节点是国际象棋大师的姓氏。每条边从白方指向黑方,并包含选定的比赛信息。
chess_pgn_graph
中的关键语句如下:
G.add_edge(white, black, game_info)
其中 game_info
是一个描述每场比赛的 dict
。
Loaded 685 chess games between 25 players
Note the disconnected component consisting of:
['Korchnoi, Viktor L', 'Karpov, Anatoly', 'Kasparov, Gary']
From a total of 237 different openings,
the following games used the Sicilian opening
with the Najdorff 7...Qb6 "Poisoned Pawn" variation.
Spassky, Boris V vs Fischer, Robert J
Event: World Championship 28th
Site: Reykjavik ISL
Date: 1972.07.25
Round: 7
Result: 1/2-1/2
WhiteElo: 2660
BlackElo: 2785
ECO: B97
EventDate: 1972.07.11
Spassky, Boris V vs Fischer, Robert J
Event: World Championship 28th
Site: Reykjavik ISL
Date: 1972.08.06
Round: 11
Result: 1-0
WhiteElo: 2660
BlackElo: 2785
ECO: B97
EventDate: 1972.07.11
findfont: Font family 'Helvetica' not found.
findfont: Font family 'Helvetica' not found.
findfont: Font family 'Helvetica' not found.
findfont: Font family 'Helvetica' not found.
import matplotlib.pyplot as plt
import networkx as nx
# 标签名称,指定应显示的游戏信息
# 存储在每个双字母组合边的字典中
game_details = ["Event", "Date", "Result", "ECO", "Site"]
def chess_pgn_graph(pgn_file="chess_masters_WCC.pgn.bz2"):
"""读取pgn_file中的pgn格式国际象棋对局。
以.bz2结尾的文件名将被解压缩。
返回由国际象棋对局连接的玩家的MultiDiGraph。
边包含以字典形式存储的对局数据。
"""
import bz2
G = nx.MultiDiGraph()
game = {}
with bz2.BZ2File(pgn_file) as datafile:
lines = [line.decode().rstrip("\r\n") for line in datafile]
for line in lines:
if line.startswith("["):
tag, value = line[1:-1].split(" ", 1)
game[str(tag)] = value.strip('"')
else:
# 标签集后的空行表示
# 我们已完成读取游戏信息
if game:
white = game.pop("White")
black = game.pop("Black")
G.add_edge(white, black, **game)
game = {}
return G
G = chess_pgn_graph()
print(
f"Loaded {G.number_of_edges()} chess games between {G.number_of_nodes()} players\n"
)
# 识别无向图的连通分量
H = G.to_undirected()
Gcc = [H.subgraph(c) for c in nx.connected_components(H)]
if len(Gcc) > 1:
print(f"Note the disconnected component consisting of:\n{Gcc[1].nodes()}")
# 查找所有使用B97开局(根据ECO描述)的游戏
openings = {game_info["ECO"] for (white, black, game_info) in G.edges(data=True)}
print(f"\nFrom a total of {len(openings)} different openings,")
print("the following games used the Sicilian opening")
print('with the Najdorff 7...Qb6 "Poisoned Pawn" variation.\n')
for white, black, game_info in G.edges(data=True):
if game_info["ECO"] == "B97":
summary = f"{white} vs {black}\n"
for k, v in game_info.items():
summary += f" {k}: {v}\n"
summary += "\n"
print(summary)
# 创建没有多重边的新无向图 H
H = nx.Graph(G)
# 边宽度与游戏次数成正比
edgewidth = [len(G.get_edge_data(u, v)) for u, v in H.edges()]
# 节点大小与赢得的比赛数量成正比
wins = dict.fromkeys(G.nodes(), 0.0)
for u, v, d in G.edges(data=True):
r = d["Result"].split("-")
if r[0] == "1":
wins[u] += 1.0
elif r[0] == "1/2":
wins[u] += 0.5
wins[v] += 0.5
else:
wins[v] += 1.0
nodesize = [wins[v] * 50 for v in H]
# 生成用于可视化的布局
pos = nx.kamada_kawai_layout(H)
# 手动调整以限制可视化中节点标签的重叠
pos["Reshevsky, Samuel H"] += (0.05, -0.10)
pos["Botvinnik, Mikhail M"] += (0.03, -0.06)
pos["Smyslov, Vassily V"] += (0.05, -0.03)
fig, ax = plt.subplots(figsize=(12, 12))
# 可视化图组件
nx.draw_networkx_edges(H, pos, alpha=0.3, width=edgewidth, edge_color="m")
nx.draw_networkx_nodes(H, pos, node_size=nodesize, node_color="#210070", alpha=0.9)
label_options = {"ec": "k", "fc": "white", "alpha": 0.7}
nx.draw_networkx_labels(H, pos, font_size=14, bbox=label_options)
# 标题/图例
font = {"fontname": "Helvetica", "color": "k", "fontweight": "bold", "fontsize": 14}
ax.set_title("World Chess Championship Games: 1886 - 1985", font)
# 更改图例的字体颜色
font["color"] = "r"
ax.text(
0.80,
0.10,
"edge width = # games played",
horizontalalignment="center",
transform=ax.transAxes,
fontdict=font,
)
ax.text(
0.80,
0.06,
"node size = # games won",
horizontalalignment="center",
transform=ax.transAxes,
fontdict=font,
)
# 调整图形大小以提高标签可读性
ax.margins(0.1, 0.05)
fig.tight_layout()
plt.axis("off")
plt.show()
Total running time of the script: (0 minutes 0.824 seconds)