Go: package main

import "fmt"

var h, w int
var maze [][]byte

var visited [][]bool

type dice struct {
	top, bottom, right, left, up, down int
}

func move(d *dice, dir byte) {
	now := d.top
	switch dir {
	case 'N':
		d.top = d.down
		d.down = d.bottom
		d.bottom = d.up
		d.up = now
	case 'S':
		d.top = d.up
		d.up = d.bottom
		d.bottom = d.down
		d.down = now
	case 'W':
		d.top = d.right
		d.right = d.bottom
		d.bottom = d.left
		d.left = now
	case 'E':
		d.top = d.left
		d.left = d.bottom
		d.bottom = d.right
		d.right = now
	}
}

var dx = [4]int{1, -1, 0, 0}
var dy = [4]int{0, 0, 1, -1}

func dfs(x, y int, d *dice) bool {
	// fmt.Printf("dfs(%d,%d) en: dice.bottom=%d\n", x, y, d.bottom)
	visited[y][x] = true // (x,y)に到達した
	if (y == h-1) && (x == w-1) {
		return true
	}
	// 4方のマス目に進めるかどうかチェック
	for i := 0; i < 4; i++ {
		nx := x + dx[i]
		ny := y + dy[i]
		dir := []byte{'E', 'W', 'S', 'N'}
		dc := *d
		move(&dc, dir[i]) // 動かしてみる
		if 0 <= nx && nx < w && 0 <= ny && ny < h && maze[ny][nx] != '#' && !visited[ny][nx] && dc.bottom == int(maze[ny][nx]-'0') {
			// fmt.Printf("move dir %c, new bottom=%d, move to (nx, ny)=(%d,%d)\n", dir[i], dc.bottom, nx, ny)
			if dfs(nx, ny, &dc) {
				return true
			}
		} else {
			continue
		}
	}
	return false
}

func main() {
	fmt.Scan(&h, &w)
	maze = make([][]byte, h)
	visited = make([][]bool, h)
	for i := 0; i < h; i++ {
		visited[i] = make([]bool, w)
		maze[i] = make([]byte, w)
		fmt.Scan(&maze[i])
	}
	// fmt.Println("h", h, "w", w, "maze", maze)
	// visited[0][0] = true
	var d dice
	d.bottom = 6
	d.top = 1
	d.right = 3
	d.left = 4
	d.down = 2
	d.up = 5
	if dfs(0, 0, &d) {
		fmt.Println("YES")
	} else {
		fmt.Println("NO")
	}
}

C++: #include <bits/stdc++.h>
using namespace std;
bool debug = false;
int main(){
  int H, W;
  cin >> H >> W;
  vector<vector<char>> s(H, vector<char>(W));
  for (int i = 0; i < H; i++){
    for (int j = 0; j < W; j++){
      cin >> s[i][j];
    }
  }
  vector<vector<bool>> M(H + 2, vector<bool>(W + 2, 0));
  for (int i = 0; i < H; i++){
    for (int j = 0; j < W; j++){
      if (i % 4 == 0 && j % 4 == 0 && s[i][j] == '6'){
        M[i + 1][j + 1] = true;
      }
      if (i % 4 == 2 && j % 4 == 2 && s[i][j] == '6'){
        M[i + 1][j + 1] = true;
      }
      if (i % 4 == 0 && j % 4 == 2 && s[i][j] == '1'){
        M[i + 1][j + 1] = true;
      }
      if (i % 4 == 2 && j % 4 == 0 && s[i][j] == '1'){
        M[i + 1][j + 1] = true;
      }
      if (i % 4 == 1 && j % 2 == 0 && s[i][j] == '2'){
        M[i + 1][j + 1] = true;
      }
      if (i % 4 == 3 && j % 2 == 0 && s[i][j] == '5'){
        M[i + 1][j + 1] = true;
      }
      if (i % 2 == 0 && j % 4 == 1 && s[i][j] == '3'){
        M[i + 1][j + 1] = true;
      }
      if (i % 2 == 0 && j % 4 == 3 && s[i][j] == '4'){
        M[i + 1][j + 1] = true;
      }
    }
  }
  if (debug){
    for (int i = 0; i < H + 2; i++){
      for (int j = 0; j < W + 2; j++){
        cout << M[i][j];
      }
      cout << endl;
    }
  }
  queue<pair<int, int>> Q;
  Q.push(make_pair(1, 1));
  vector<vector<bool>> U(H + 2, vector<bool>(W + 2, false));
  U[1][1] = true;
  while (!Q.empty()){
    int y = Q.front().first;
    int x = Q.front().second;
    if (debug){
      cout << "y=" << y <<",x=" << x << endl;
    }
    Q.pop();
    if (M[y + 1][x] && !U[y + 1][x]){
      Q.push(make_pair(y + 1, x));
      U[y + 1][x] = true;
    }
    if (M[y - 1][x] && !U[y - 1][x]){
      Q.push(make_pair(y - 1, x));
      U[y - 1][x] = true;
    }
    if (M[y][x + 1] && !U[y][x + 1]){
      Q.push(make_pair(y, x + 1));
      U[y][x + 1] = true;
    }
    if (M[y][x - 1] && !U[y][x - 1]){
      Q.push(make_pair(y, x - 1));
      U[y][x - 1] = true;
    }
  }
  if (U[H][W]){
    cout << "YES" << endl;
  } else {
    cout << "NO" << endl;
  }
}

C++: #include <bits/stdc++.h>
using namespace std;
using LL = long long int;
#define incID(i, l, r) for(int i = (l)    ; i <  (r); ++i)
#define decID(i, l, r) for(int i = (r) - 1; i >= (l); --i)
#define incII(i, l, r) for(int i = (l)    ; i <= (r); ++i)
#define decII(i, l, r) for(int i = (r)    ; i >= (l); --i)
#define inc(i, n)  incID(i, 0, n)
#define dec(i, n)  decID(i, 0, n)
#define inc1(i, n) incII(i, 1, n)
#define dec1(i, n) decII(i, 1, n)
#define inID(v, l, r) ((l) <= (v) && (v) <  (r))
#define inII(v, l, r) ((l) <= (v) && (v) <= (r))
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
#define FR front()
#define BA back()
#define ALL(v) v.begin(), v.end()
#define RALL(v) v.rbegin(), v.rend()
auto setmin   = [](auto & a, auto b) { return (b <  a ? a = b, true : false); };
auto setmax   = [](auto & a, auto b) { return (b >  a ? a = b, true : false); };
auto setmineq = [](auto & a, auto b) { return (b <= a ? a = b, true : false); };
auto setmaxeq = [](auto & a, auto b) { return (b >= a ? a = b, true : false); };
#define SI(v) static_cast<int>(v.size())
#define RF(e, v) for(auto & e: v)
#define until(e) while(! (e))
#define if_not(e) if(! (e))
#define ef else if
#define UR assert(false)
#define IN(T, ...) T __VA_ARGS__; IN_(__VA_ARGS__);
void IN_() { };
template<typename T, typename ... U> void IN_(T & a, U & ... b) { cin >> a; IN_(b ...); };
template<typename T> void OUT(T && a) { cout << a << endl; }
template<typename T, typename ... U> void OUT(T && a, U && ... b) { cout << a << " "; OUT(b ...); }

// ---- ----

class UnionFind {
private:
	int n, s;
	vector<int> t;
	vector<vector<int>> v;
public:
	UnionFind(int arg_n = 0) { init(arg_n); }
	void init(int arg_n) {
		n = s = arg_n;
		t.clear();
		v.clear();
		inc(i, n) { t.PB(i); v.EB(1, i); }
	}
	int get_n() { return n; }
	int size() { return s; }
	int id(int x) { return t.at(x); }
	const vector<vector<int>> & get_v() { return v; }
	bool unite(int x, int y) {
		x = id(x);
		y = id(y);
		if(x == y) { return false; }
		if(v[x].size() < v[y].size()) { swap(x, y); }
		for(auto & e: v[y]) { v[x].PB(e); t[e] = x; }
		v[y].clear();
		s--;
		return true;
	}
	bool same(int x, int y) { return (id(x) == id(y)); }
	const vector<int> & operator[](int x) { return v[id(x)]; }
	friend ostream & operator<<(ostream & os, const UnionFind & uf) {
		inc(i, uf.n) { os << i << ": "; for(auto & e: uf.v[i]) { os << e << " "; } os << "\n"; }
		return os;
	}
};

// ---- ----

template<typename T> istream & operator>>(istream & s, vector<T> & v) { RF(e, v) { s >> e; } return s; }
template<typename T> ostream & operator<<(ostream & s, vector<T> const & v) {
	inc(i, SI(v)) { s << (i == 0 ? "" : " ") << v[i]; }
	return s;
}

int main() {
	IN(int, h, w);
	vector<string> s(h);
	cin >> s;
	
	vector<string> t = {
		"6314",
		"2-2-",
		"1364",
		"5-5-",
	};
	
	inc(i, h) {
	inc(j, w) {
		if(s[i][j] == t[i % 4][j % 4]) { s[i][j] = 'O'; }
	}
	}
	
	auto id = [&](int i, int j) { return w * i + j; };
	UnionFind uf(h * w);
	inc(i, h) {
	inc(j, w) {
		if_not(s[i][j] == 'O') { continue; }
		if(i + 1 < h && s[i + 1][j] == 'O') { uf.unite(id(i, j), id(i + 1, j)); }
		if(j + 1 < w && s[i][j + 1] == 'O') { uf.unite(id(i, j), id(i, j + 1)); }
	}
	}
	
	OUT(uf.same(id(0, 0), id(h - 1, w - 1)) ? "YES" : "NO");
}