Somar &
Vencer
Da planilha de notas ao tabuleiro 3×3 — extraímos médias por linha e por coluna, depois modelamos um Jogo da Velha completo em terminal.
Da planilha de notas ao tabuleiro 3×3 — extraímos médias por linha e por coluna, depois modelamos um Jogo da Velha completo em terminal.
(i, j)m[0] é a primeira linha, m[N-1] a última[[0]*M for _ in range(N)], nunca [[0]*M]*Nfor i externo (linhas), for j interno (colunas)A3Calcular somas e médias por linha, por coluna e total — agregações 2D
A3Aplicar o truque do zip(*m) para transpor linhas em colunas em uma linha de código
A4Modelar um tabuleiro 3×3 em matriz e validar jogadas (intervalo, célula livre, alternância)
A4Implementar a verificação de vitória nas 8 trincas — linhas, colunas e duas diagonais
Você está construindo o módulo de notas do sistema escolar. A turma tem 3 alunos e 4 provas. As notas chegam numa tabela como esta:
Três perguntas que a coordenação faz toda reunião:
Todas essas respostas são agregações: reduzir muitos valores em um valor. E todas seguem o mesmo padrão — varrer a matriz e acumular.
Toda agregação responde uma pergunta simples: "como reduzir muitos valores em um?". Em matrizes, existem três eixos de redução. Cada um produz um resultado de forma diferente.
Reduz cada linha num único número. Em uma matriz N×M, gera N resultados — um por linha. Soma da Ana, soma da Bia, soma do Caio.
resultado[i] = soma(m[i])
Reduz cada coluna num único número. Em N×M, gera M resultados — um por coluna. Soma da P1, soma da P2, soma da P3, soma da P4.
resultado[j] = soma da coluna j
Reduz a matriz inteira num único número. Gera 1 resultado. Soma de todas as notas da turma, máximo global, mínimo global.
resultado = soma de m[i][j]
Comece pelo caso mais simples: a soma de cada linha. A chave é reiniciar o acumulador dentro do laço externo — assim cada linha tem seu próprio total.
// Pseudocódigo — soma por linha
para i de 0 ate n_linhas - 1 faca
soma_linha <- 0 // ⚠ zera A CADA linha
para j de 0 ate n_colunas - 1 faca
soma_linha <- soma_linha + m[i][j]
fim_para
escrever( "Linha ", i, " = ", soma_linha )
fim_para
# Python — soma por linha
m = [[8.0, 7.0, 9.0, 8.0],
[6.0, 7.5, 5.0, 8.5],
[9.0, 8.0, 7.0, 10.0]]
for i in range(len(m)):
soma_linha = 0 # zera a cada linha
for j in range(len(m[i])):
soma_linha += m[i][j]
print(f"Linha {i} = {soma_linha}")
soma_linha = 0 fora do laço externo. Resultado: a variável acumula tudo, e a "soma da linha 2" vira a soma de todas as linhas até a 2. Sempre que quiser somar por linha, zere dentro do for i.
zip(*m)Para somar por coluna, há dois caminhos. O longo inverte os laços (j externo, i interno). O pythônico usa zip(*m), que transpõe a matriz — linhas viram colunas, e o problema cai no caso anterior.
# Caminho 1 — manual, laços invertidos
n_lin = len(m)
n_col = len(m[0])
for j in range(n_col): # coluna externa
soma_col = 0
for i in range(n_lin): # linha interna
soma_col += m[i][j]
print(f"Coluna {j} = {soma_col}")
# Caminho 2 — pythônico, com zip
for j, col in enumerate(zip(*m)):
print(f"Coluna {j} = {sum(col)}")
# zip(*m) "desempilha" as linhas
# e empacota item por item:
# linha 0: 8.0 7.0 9.0 8.0
# linha 1: 6.0 7.5 5.0 8.5
# linha 2: 9.0 8.0 7.0 10.0
# vira:
# col 0: (8.0, 6.0, 9.0)
# col 1: (7.0, 7.5, 8.0)
# col 2: (9.0, 5.0, 7.0)
# col 3: (8.0, 8.5, 10.0)
*m: o asterisco desempacota a lista — em vez de passar m (uma lista de listas), passamos cada linha como argumento separado para zip. O zip então agrupa "o primeiro de cada", "o segundo de cada"… — efetivamente trocando linhas por colunas. É a transposta em uma linha.
Aplique os dois padrões na planilha real. Calcule a média de cada aluno e a média de cada prova com um único bloco de código.
notas = [[8.0, 7.0, 9.0, 8.0], # Ana
[6.0, 7.5, 5.0, 8.5], # Bia
[9.0, 8.0, 7.0, 10.0]] # Caio
alunos = ["Ana", "Bia", "Caio"]
# Médias por aluno (por linha)
for i in range(len(notas)):
media = sum(notas[i]) / len(notas[i])
print(f"{alunos[i]:5s}: média = {media:.2f}")
# Médias por prova (por coluna, com zip)
for j, col in enumerate(zip(*notas), start=1):
print(f"P{j}: média = {sum(col)/len(col):.2f}")
Ana : média = 8.00
Bia : média = 6.75
Caio : média = 8.50
P1: média = 7.67
P2: média = 7.50
P3: média = 7.00
P4: média = 8.83
── 3 médias por aluno (linha)
── 4 médias por prova (coluna)
── em uma única matriz 3×4
sum(linha) / len(linha) dá a média de uma linha; sum(col) / len(col) com zip(*m) dá a média de uma coluna. Mesma fórmula, mudou só o eixo de iteração.
Saber agregar dados de uma matriz é metade da história. A outra metade — talvez mais empolgante — é usar matrizes para modelar o mundo. Tabuleiros, mapas, grades de pixels, mapas de calor, espaços de jogos. Vamos fechar a semana construindo um Jogo da Velha completo em terminal: a matriz é o tabuleiro, e cada jogada lê, valida, atualiza e verifica vitória.
Num tabuleiro 3×3, há exatamente oito combinações de três células que vencem o jogo: 3 linhas, 3 colunas e 2 diagonais. Visualmente:
2N + 2 — N linhas, N colunas, 2 diagonais. Para o Jogo da Velha (N=3), são 8. Para um Gomoku 15×15, seriam 32 (sem contar as trincas internas das linhas, que são outra discussão).
O desafio: programa em terminal onde dois jogadores alternam (X e O), o sistema valida cada jogada e checa vitória. Anatomia em 7 passos.
Modele o tabuleiro. Matriz 3×3 de '.' usando list comprehension — sem aliasing.
Imprima o tabuleiro. Percorra linha a linha, separando células com | ; entre linhas, mostre uma linha de -.
Leia a jogada. Peça linha e coluna (0 a 2). Valide: intervalo correto E célula é '.'. Se inválido, peça de novo.
Atualize. tab[i][j] = jogador_atual — escreve X ou O na célula validada.
Cheque vitória. Percorra as 8 trincas. Se alguma estiver com 3 símbolos iguais do jogador atual, ele venceu.
Alterne o jogador. jogador = 'O' if jogador == 'X' else 'X'.
Trate o empate. Se 9 jogadas se passaram sem vitória, é empate. O for/else de Python ajuda: o else só roda quando o for termina sem break.
0 ≤ i, j ≤ 2 não basta — sem checar tab[i][j] == '.', o jogador O pode sobrescrever uma jogada do X. Sempre confira as duas condições.
Antes do laço principal, prepare duas funções: imprime(), que mostra o tabuleiro, e venceu(), que retorna True se o jogador atual fechou alguma das 8 trincas.
def imprime(tab):
for linha in tab:
print(' | '.join(linha))
print('-' * 9)
def venceu(tab, j):
# 3 linhas
for i in range(3):
if tab[i][0] == tab[i][1] == tab[i][2] == j:
return True
# 3 colunas
for c in range(3):
if tab[0][c] == tab[1][c] == tab[2][c] == j:
return True
# 2 diagonais
if tab[0][0] == tab[1][1] == tab[2][2] == j:
return True
if tab[0][2] == tab[1][1] == tab[2][0] == j:
return True
return False
Estado de exemplo — X venceu pela diagonal principal:
venceu(tab, 'X') retorna True ao testar a diagonal principal — tab[0][0] == tab[1][1] == tab[2][2] == 'X'.
a == b == c == d: Python avalia em cadeia — equivale a (a == b) and (b == c) and (c == d). Se qualquer par for diferente (ou se for '.'), o resultado é False e a trinca não vence. Isso já filtra automaticamente as células vazias.
O laço usa for jogada in range(9) — no máximo 9 jogadas até o tabuleiro lotar. O else do for trata o empate. Dentro do laço, um while True insiste até receber uma jogada válida.
tab = [['.' for _ in range(3)] for _ in range(3)]
jogador = 'X'
for jogada in range(9):
imprime(tab)
while True:
i = int(input(f"{jogador} - linha (0-2): "))
j = int(input(f"{jogador} - coluna (0-2): "))
if 0 <= i < 3 and 0 <= j < 3 and tab[i][j] == '.':
break
print("Jogada inválida, tente outra.")
tab[i][j] = jogador
if venceu(tab, jogador):
imprime(tab)
print(f"{jogador} venceu!")
break
jogador = 'O' if jogador == 'X' else 'X'
else:
imprime(tab)
print("Empate!")
. | . | .
---------
. | . | .
---------
. | . | .
---------
X - linha (0-2): 1
X - linha (0-2): 1
. | . | .
---------
. | X | .
---------
. | . | .
---------
O - linha (0-2): 0
O - linha (0-2): 0
── …jogo continua…
X venceu!
3×3 com list comprehension (sem aliasing!). Não use numpy. A função venceu deve testar todas as 8 trincas. Se uma jogada for inválida, peça de novo — não pule a vez do jogador.
Em um tabuleiro de Jogo da Velha 3×3, quantas combinações distintas de três células permitem vencer o jogo, contando linhas, colunas e diagonais?
3 + 3 + 2 = 8. Generalizando para um tabuleiro N×N, são 2N + 2 trincas — o que dá 8 para N=3.
sum(m[i])zip(*m) para transporsum(sum(linha) for linha in m)m[i][j]a == b == c == j testa cada umaelse só roda quando o for termina sem break; perfeito para detectar empateFunções e procedimentos — encapsulamento, parâmetros, retorno e escopo. Vamos transformar laços e blocos repetidos em ferramentas reutilizáveis. Fecha a unidade de estruturas de dados e abre a porta para programação modular.
PYTHON SOFTWARE FOUNDATION. Built-in Functions — zip(). The Python Standard Library, [s.d.]. Disponível em: docs.python.org/3/library/functions.html#zip. Acesso em: 20 mai. 2026.
PYTHON SOFTWARE FOUNDATION. Compound Statements — for/else. The Python Language Reference, [s.d.]. Disponível em: docs.python.org/3/reference/compound_stmts.html. Acesso em: 20 mai. 2026.
RIBEIRO, M. Estrutura de dados — Matrizes em Python. DEV Community, 24 jun. 2023. Disponível em: dev.to/mhribeiro/matrizes-em-python-2e9j. Acesso em: 20 mai. 2026.
Base institucional: SEDUC-SP. Educação Profissional Paulista — Técnico em Desenvolvimento de Sistemas. Componente 1, Unidade 4, Semana 12 (SISANO1C1B2S12A3 e SISANO1C1B2S12A4).
Identidade visual · adaptação para apresentação HTML por Guilherme Antunes